1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Atlantic Network Driver |
3 | * |
4 | * Copyright (C) 2014-2019 aQuantia Corporation |
5 | * Copyright (C) 2019-2020 Marvell International Ltd. |
6 | */ |
7 | |
8 | /* File aq_hw_utils.c: Definitions of helper functions used across |
9 | * hardware layer. |
10 | */ |
11 | |
12 | #include "aq_hw_utils.h" |
13 | |
14 | #include <linux/io-64-nonatomic-lo-hi.h> |
15 | |
16 | #include "aq_hw.h" |
17 | #include "aq_nic.h" |
18 | |
19 | void aq_hw_write_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk, |
20 | u32 shift, u32 val) |
21 | { |
22 | if (msk ^ ~0) { |
23 | u32 reg_old, reg_new; |
24 | |
25 | reg_old = aq_hw_read_reg(hw: aq_hw, reg: addr); |
26 | reg_new = (reg_old & (~msk)) | (val << shift); |
27 | |
28 | if (reg_old != reg_new) |
29 | aq_hw_write_reg(hw: aq_hw, reg: addr, value: reg_new); |
30 | } else { |
31 | aq_hw_write_reg(hw: aq_hw, reg: addr, value: val); |
32 | } |
33 | } |
34 | |
35 | u32 aq_hw_read_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk, u32 shift) |
36 | { |
37 | return ((aq_hw_read_reg(hw: aq_hw, reg: addr) & msk) >> shift); |
38 | } |
39 | |
40 | u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg) |
41 | { |
42 | u32 value = readl(addr: hw->mmio + reg); |
43 | |
44 | if (value == U32_MAX && |
45 | readl(addr: hw->mmio + hw->aq_nic_cfg->aq_hw_caps->hw_alive_check_addr) == U32_MAX) |
46 | aq_utils_obj_set(flags: &hw->flags, AQ_HW_FLAG_ERR_UNPLUG); |
47 | |
48 | return value; |
49 | } |
50 | |
51 | void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value) |
52 | { |
53 | writel(val: value, addr: hw->mmio + reg); |
54 | } |
55 | |
56 | /* Most of 64-bit registers are in LSW, MSW form. |
57 | Counters are normally implemented by HW as latched pairs: |
58 | reading LSW first locks MSW, to overcome LSW overflow |
59 | */ |
60 | u64 aq_hw_read_reg64(struct aq_hw_s *hw, u32 reg) |
61 | { |
62 | u64 value = U64_MAX; |
63 | |
64 | if (hw->aq_nic_cfg->aq_hw_caps->op64bit) |
65 | value = readq(addr: hw->mmio + reg); |
66 | else |
67 | value = lo_hi_readq(addr: hw->mmio + reg); |
68 | |
69 | if (value == U64_MAX && |
70 | readl(addr: hw->mmio + hw->aq_nic_cfg->aq_hw_caps->hw_alive_check_addr) == U32_MAX) |
71 | aq_utils_obj_set(flags: &hw->flags, AQ_HW_FLAG_ERR_UNPLUG); |
72 | |
73 | return value; |
74 | } |
75 | |
76 | void aq_hw_write_reg64(struct aq_hw_s *hw, u32 reg, u64 value) |
77 | { |
78 | if (hw->aq_nic_cfg->aq_hw_caps->op64bit) |
79 | writeq(val: value, addr: hw->mmio + reg); |
80 | else |
81 | lo_hi_writeq(val: value, addr: hw->mmio + reg); |
82 | } |
83 | |
84 | int aq_hw_err_from_flags(struct aq_hw_s *hw) |
85 | { |
86 | int err = 0; |
87 | |
88 | if (aq_utils_obj_test(flags: &hw->flags, AQ_HW_FLAG_ERR_UNPLUG)) { |
89 | err = -ENXIO; |
90 | goto err_exit; |
91 | } |
92 | if (aq_utils_obj_test(flags: &hw->flags, AQ_HW_FLAG_ERR_HW)) { |
93 | err = -EIO; |
94 | goto err_exit; |
95 | } |
96 | |
97 | err_exit: |
98 | return err; |
99 | } |
100 | |
101 | int aq_hw_num_tcs(struct aq_hw_s *hw) |
102 | { |
103 | switch (hw->aq_nic_cfg->tc_mode) { |
104 | case AQ_TC_MODE_8TCS: |
105 | return 8; |
106 | case AQ_TC_MODE_4TCS: |
107 | return 4; |
108 | default: |
109 | break; |
110 | } |
111 | |
112 | return 1; |
113 | } |
114 | |
115 | int aq_hw_q_per_tc(struct aq_hw_s *hw) |
116 | { |
117 | switch (hw->aq_nic_cfg->tc_mode) { |
118 | case AQ_TC_MODE_8TCS: |
119 | return 4; |
120 | case AQ_TC_MODE_4TCS: |
121 | return 8; |
122 | default: |
123 | return 4; |
124 | } |
125 | } |
126 | |