Loading drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +30 −15 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ #include <linux/netdevice.h> #include <linux/moduleparam.h> #define ATL_VERSION "1.0.19" #define ATL_VERSION "1.0.20" struct atl_nic; Loading Loading @@ -220,9 +220,8 @@ struct atl_nic { int nvecs; struct atl_hw hw; unsigned flags; unsigned long state; uint32_t priv_flags; struct timer_list link_timer; struct timer_list work_timer; int max_mtu; int requested_nvecs; int requested_rx_size; Loading @@ -249,13 +248,6 @@ enum atl_nic_flags { ATL_FL_WOL = BIT(1), }; enum atl_nic_state { ATL_ST_UP, ATL_ST_CONFIGURED, ATL_ST_ENABLED, ATL_ST_WORK_SCHED, }; #define ATL_PF(_name) ATL_PF_ ## _name #define ATL_PF_BIT(_name) ATL_PF_ ## _name ## _BIT #define ATL_DEF_PF_BIT(_name) ATL_PF_BIT(_name) = BIT(ATL_PF(_name)) Loading Loading @@ -335,18 +327,37 @@ extern int atl_enable_msi; #define atl_nic_err(fmt, args...) \ dev_err(&nic->hw.pdev->dev, fmt, ## args) #define atl_dev_init_warn(fmt, args...) \ do { \ if (hw) \ atl_dev_warn(fmt, ## args); \ else \ printk(KERN_WARNING "%s: " fmt, atl_driver_name, ##args); \ } while(0) #define atl_dev_init_err(fmt, args...) \ do { \ if (hw) \ atl_dev_warn(fmt, ## args); \ else \ printk(KERN_ERR "%s: " fmt, atl_driver_name, ##args); \ } while(0) #define atl_module_param(_name, _type, _mode) \ module_param_named(_name, atl_ ## _name, _type, _mode) static inline void atl_intr_enable_non_ring(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; uint32_t mask = hw->intr_mask; #ifdef CONFIG_ATLFWD_FWD mask |= (uint32_t)(nic->fwd.msi_map); #endif atl_intr_enable(hw, mask); atl_intr_enable(hw, hw->non_ring_intr_mask); } static inline void atl_intr_disable_non_ring(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; atl_intr_disable(hw, hw->non_ring_intr_mask); } netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev); Loading @@ -360,6 +371,7 @@ int atl_setup_datapath(struct atl_nic *nic); void atl_clear_datapath(struct atl_nic *nic); int atl_start_rings(struct atl_nic *nic); void atl_stop_rings(struct atl_nic *nic); void atl_clear_rdm_cache(struct atl_nic *nic); int atl_alloc_rings(struct atl_nic *nic); void atl_free_rings(struct atl_nic *nic); irqreturn_t atl_ring_irq(int irq, void *priv); Loading Loading @@ -403,5 +415,8 @@ int atl_mdio_write(struct atl_hw *hw, uint8_t prtad, uint8_t mmd, void atl_refresh_rxfs(struct atl_nic *nic); void atl_schedule_work(struct atl_nic *nic); int atl_hwmon_init(struct atl_nic *nic); int atl_update_thermal(struct atl_hw *hw); int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val); int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal); #endif drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c +447 −59 Original line number Diff line number Diff line Loading @@ -36,17 +36,8 @@ struct atl_link_type atl_link_types[] = { const int atl_num_rates = ARRAY_SIZE(atl_link_types); static inline void atl_lock_fw(struct atl_hw *hw) { mutex_lock(&hw->mcp.lock); } static inline void atl_unlock_fw(struct atl_hw *hw) { mutex_unlock(&hw->mcp.lock); } static int atl_fw1_wait_fw_init(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw1_wait_fw_init(struct atl_hw *hw) { uint32_t hostData_addr; uint32_t id, new_id; Loading Loading @@ -79,7 +70,8 @@ static int atl_fw1_wait_fw_init(struct atl_hw *hw) return 0; } static int atl_fw2_wait_fw_init(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw2_wait_fw_init(struct atl_hw *hw) { uint32_t reg; Loading Loading @@ -148,6 +140,8 @@ static struct atl_link_type *atl_fw1_check_link(struct atl_hw *hw) return link; } static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts); static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) { struct atl_link_type *link; Loading @@ -162,58 +156,91 @@ static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) high = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)); link = atl_parse_fw_bits(hw, low, high, 1); if (!link) goto unlock; __atl_fw2_thermal_check(hw, low); /* Thermal check might have reset link due to throttling */ link = lstate->link; if (link) { if (high & atl_fw2_pause) fc |= atl_fc_rx; if (high & atl_fw2_asym_pause) fc |= atl_fc_tx; } lstate->fc.cur = fc; unlock: atl_unlock_fw(hw); return link; } static int atl_fw1_get_link_caps(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw1_get_link_caps(struct atl_hw *hw) { return 0; } static int atl_fw2_get_link_caps(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw2_get_link_caps(struct atl_hw *hw) { uint32_t fw_stat_addr = hw->mcp.fw_stat_addr; struct atl_mcp *mcp = &hw->mcp; uint32_t fw_stat_addr = mcp->fw_stat_addr; struct atl_link_type *rate; unsigned int supported = 0; uint32_t caps[2]; uint32_t caps[2], mask = atl_fw2_pause_mask | atl_fw2_link_drop; int i, ret; atl_lock_fw(hw); atl_dev_dbg("Host data struct addr: %#x\n", fw_stat_addr); ret = atl_read_mcp_mem(hw, fw_stat_addr + atl_fw2_stat_lcaps, caps, 8); if (ret) goto unlock; return ret; for (i = 0; i < atl_num_rates; i++) if (atl_link_types[i].fw_bits[1] & caps[0]) { mcp->caps_low = caps[0]; mcp->caps_high = caps[1]; atl_dev_dbg("Got link caps: %#x %#x\n", caps[0], caps[1]); atl_for_each_rate(i, rate) { uint32_t bit = rate->fw_bits[1]; if (bit & caps[0]) { supported |= BIT(i); if (atl_link_types[i].fw_bits[1] & caps[1]) if (bit & caps[1]) { supported |= BIT(i + ATL_EEE_BIT_OFFT); mask |= bit; } } } mcp->req_high_mask = ~mask; hw->link_state.supported = supported; hw->link_state.lp_lowest = fls(supported) - 1; unlock: atl_unlock_fw(hw); return ret; } static inline unsigned int atl_link_adv(struct atl_link_state *lstate) { return lstate->force_off ? 0 : lstate->advertized; struct atl_hw *hw = container_of(lstate, struct atl_hw, link_state); if (lstate->force_off) return 0; if (lstate->thermal_throttled && hw->thermal.flags & atl_thermal_throttle) /* FW doesn't provide raw LP's advertized rates, only * the rates adverized both by us and LP. Here we * advertize not just the throttled_to rate, but also * all the lower rates as well. That way if LP changes * or dynamically starts to adverize a lower rate than * throttled_to, we will notice that in * atl_fw2_thermal_check() and switch to that lower * rate there. */ return BIT(lstate->throttled_to + 1) - 1; return lstate->advertized; } static inline bool atl_fw1_set_link_needed(struct atl_link_state *lstate) Loading Loading @@ -276,16 +303,14 @@ static void atl_fw1_set_link(struct atl_hw *hw, bool force) atl_unlock_fw(hw); } static void atl_fw2_set_link(struct atl_hw *hw, bool force) /* fw lock must be held */ static void __atl_fw2_set_link(struct atl_hw *hw) { struct atl_link_state *lstate = &hw->link_state; uint32_t hi_bits = 0; uint32_t hi_bits; uint64_t bits; if (!force && !atl_fw2_set_link_needed(lstate)) return; atl_lock_fw(hw); hi_bits = hw->mcp.req_high & hw->mcp.req_high_mask; if (lstate->fc.req & atl_fc_rx) hi_bits |= atl_fw2_pause | atl_fw2_asym_pause; Loading @@ -294,15 +319,26 @@ static void atl_fw2_set_link(struct atl_hw *hw, bool force) hi_bits ^= atl_fw2_asym_pause; bits = atl_set_fw_bits(hw, 1); hi_bits |= bits >> 32; /* If no modes are advertized, put PHY into low-power */ if (!bits) hi_bits = atl_fw2_link_drop; hi_bits |= atl_fw2_link_drop; else hi_bits |= bits >> 32; hw->mcp.req_high = hi_bits; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_LOW), bits); atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), hi_bits); } static void atl_fw2_set_link(struct atl_hw *hw, bool force) { if (!force && !atl_fw2_set_link_needed(&hw->link_state)) return; atl_lock_fw(hw); __atl_fw2_set_link(hw); atl_unlock_fw(hw); } Loading @@ -314,6 +350,8 @@ static int atl_fw1_unsupported(struct atl_hw *hw) static int atl_fw2_restart_aneg(struct atl_hw *hw) { atl_lock_fw(hw); /* Autoneg restart is self-clearing, no need to track via * mcp->req_high */ atl_set_bits(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), BIT(31)); atl_unlock_fw(hw); return 0; Loading Loading @@ -398,15 +436,17 @@ int atl_read_mcp_word(struct atl_hw *hw, uint32_t offt, uint32_t *val) return 0; } static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) /* fw lock must be held */ static int __atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) { uint32_t req, res; int ret = 0; atl_lock_fw(hw); if (test_bit(ATL_ST_RESETTING, &hw->state)) return 0; req = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH)); req ^= atl_fw2_phy_temp; hw->mcp.req_high ^= atl_fw2_phy_temp; req = hw->mcp.req_high; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), req); busy_wait(1000, udelay(10), res, Loading @@ -414,27 +454,95 @@ static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) ((res ^ req) & atl_fw2_phy_temp) != 0); if (((res ^ req) & atl_fw2_phy_temp) != 0) { atl_dev_err("Timeout waiting for PHY temperature\n"); ret = -EIO; goto unlock; return -EIO; } ret = atl_read_fwstat_word(hw, atl_fw2_stat_temp, &res); if (ret) goto unlock; return ret; *temp = (res & 0xffff) * 1000 / 256; unlock: return ret; } static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) { int ret; atl_lock_fw(hw); ret = __atl_fw2_get_phy_temperature(hw, temp); atl_unlock_fw(hw); return ret; } /* fw lock must be held */ static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts) { bool alarm; int temp, ret; struct atl_link_state *lstate = &hw->link_state; struct atl_link_type *link = lstate->link; int lowest; alarm = !!(sts & atl_fw2_thermal_alarm); if (link) { /* ffs() / fls() number bits starting at 1 */ lowest = ffs(lstate->lp_advertized) - 1; if (lowest < lstate->lp_lowest) { lstate->lp_lowest = lowest; if (lowest < lstate->throttled_to && lstate->thermal_throttled && alarm) /* We're still thermal-throttled, and * just found out we can lower the * speed even more, so renegotiate. */ goto relink; } } else lstate->lp_lowest = fls(lstate->supported) - 1; if (alarm == lstate->thermal_throttled) return; lstate->thermal_throttled = alarm; ret = __atl_fw2_get_phy_temperature(hw, &temp); if (ret) temp = 0; else /* Temperature is in millidegrees C */ temp = (temp + 50) / 100; if (alarm) { if (temp) atl_dev_warn("PHY temperature above threshold: %d.%d\n", temp / 10, temp % 10); else atl_dev_warn("PHY temperature above threshold\n"); } else { if (temp) atl_dev_warn("PHY temperature back in range: %d.%d\n", temp / 10, temp % 10); else atl_dev_warn("PHY temperature back in range\n"); } relink: if (hw->thermal.flags & atl_thermal_throttle) { /* If throttling is enabled, renegotiate link */ lstate->link = 0; lstate->throttled_to = lstate->lp_lowest; __atl_fw2_set_link(hw); } } static struct atl_fw_ops atl_fw_ops[2] = { [0] = { .wait_fw_init = atl_fw1_wait_fw_init, .__wait_fw_init = __atl_fw1_wait_fw_init, .set_link = atl_fw1_set_link, .check_link = atl_fw1_check_link, .get_link_caps = atl_fw1_get_link_caps, .__get_link_caps = __atl_fw1_get_link_caps, .restart_aneg = atl_fw1_unsupported, .set_default_link = atl_fw1_set_default_link, .enable_wol = atl_fw1_unsupported, Loading @@ -442,10 +550,10 @@ static struct atl_fw_ops atl_fw_ops[2] = { .efuse_shadow_addr_reg = ATL_MCP_SCRATCH(FW1_EFUSE_SHADOW), }, [1] = { .wait_fw_init = atl_fw2_wait_fw_init, .__wait_fw_init = __atl_fw2_wait_fw_init, .set_link = atl_fw2_set_link, .check_link = atl_fw2_check_link, .get_link_caps = atl_fw2_get_link_caps, .__get_link_caps = __atl_fw2_get_link_caps, .restart_aneg = atl_fw2_restart_aneg, .set_default_link = atl_fw2_set_default_link, .enable_wol = atl_fw2_enable_wol, Loading @@ -454,10 +562,225 @@ static struct atl_fw_ops atl_fw_ops[2] = { }, }; /* fw lock must be held */ static int __atl_fw2_set_thermal_monitor(struct atl_hw *hw, bool enable) { struct atl_mcp *mcp = &hw->mcp; int ret; uint32_t high; if (enable) { struct atl_fw2_thermal_cfg cfg __attribute__((__aligned__(4))); cfg.msg_id = 0x17; cfg.shutdown_temp = hw->thermal.crit; cfg.high_temp = hw->thermal.high; cfg.normal_temp = hw->thermal.low; ret = atl_write_mcp_mem(hw, 0, &cfg, (sizeof(cfg) + 3) & ~3, MCP_AREA_CONFIG); if (ret) { atl_dev_err("Failed to upload thermal thresholds to firmware: %d\n", ret); return ret; } mcp->req_high |= atl_fw2_set_thermal; } else mcp->req_high &= ~atl_fw2_set_thermal; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), mcp->req_high); busy_wait(1000, udelay(10), high, atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)), !!(high & atl_fw2_set_thermal) != enable); if (!!(high & atl_fw2_set_thermal) != enable) { atl_dev_err("Timeout waiting for thermal monitoring FW request\n"); return -EIO; } return 0; } /* fw lock must be held */ static int __atl_fw2_update_thermal(struct atl_hw *hw) { struct atl_mcp *mcp = &hw->mcp; int ret = 0; bool enable = !!(hw->thermal.flags & atl_thermal_monitor); if (!enable || (mcp->req_high & atl_fw2_set_thermal)) { /* If monitoring is on and we need to change the * thresholds, we need to temporarily disable thermal * monitoring first. */ ret = __atl_fw2_set_thermal_monitor(hw, false); if (ret) return ret; } if (enable) ret = __atl_fw2_set_thermal_monitor(hw, true); /* Thresholds might have changed, recheck state. */ __atl_fw2_thermal_check(hw, atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_LOW))); return ret; } struct atl_thermal_limit { uintptr_t offset; const char *name; unsigned min; unsigned max; }; #define atl_def_thermal_limit(_name, _field, _min, _max) \ { \ .offset = offsetof(struct atl_thermal, _field), \ .name = _name, \ .min = _min, \ .max = _max, \ }, static struct atl_thermal_limit atl_thermal_limits[] = { atl_def_thermal_limit("Shutdown", crit, 108, 118) atl_def_thermal_limit("High", high, 90, 107) atl_def_thermal_limit("Normal", low, 50, 85) }; int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal) { int i; bool ignore = !!(thermal->flags & atl_thermal_ignore_lims); for (i = 0; i < ARRAY_SIZE(atl_thermal_limits); i++) { struct atl_thermal_limit *lim = &atl_thermal_limits[i]; unsigned val = *((uint8_t *)thermal + lim->offset); if (val >= lim->min && val <= lim->max) continue; if (ignore) { atl_dev_init_warn("%s temperature threshold out of range (%d - %d): %d, allowing anyway\n", lim->name, lim->min, lim->max, val); continue; } else { atl_dev_init_err("%s temperature threshold out of range (%d - %d): %d\n", lim->name, lim->min, lim->max, val); return -EINVAL; } } return 0; } int atl_update_thermal(struct atl_hw *hw) { int ret; ret = atl_verify_thermal_limits(hw, &hw->thermal); if (ret) return ret; if (test_bit(ATL_ST_RESETTING, &hw->state)) /* After reset, atl_fw_init() will apply the settings * skipped here */ return 0; atl_lock_fw(hw); ret = __atl_fw2_update_thermal(hw); atl_unlock_fw(hw); return ret; } int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val) { struct atl_thermal *thermal = &hw->thermal; unsigned flags, changed; int ret = 0; atl_lock_fw(hw); flags = thermal->flags; switch (bit) { case atl_thermal_monitor_shift: if (!val) /* Disable throttling along with monitoring */ flags &= ~atl_thermal_throttle; else if (!(hw->mcp.caps_high & atl_fw2_set_thermal)) { atl_dev_err("Thermal monitoring not supported by firmware\n"); return -EINVAL; } break; case atl_thermal_throttle_shift: if (val && !(flags & atl_thermal_monitor)) { atl_dev_err("Thermal monitoring needs to be enabled before enabling throttling\n"); ret = -EINVAL; } break; case atl_thermal_ignore_lims_shift: break; default: ret = -EINVAL; break; } if (ret) goto unlock; flags &= ~BIT(bit); flags |= val << bit; changed = flags ^ thermal->flags; thermal->flags = flags; if (test_bit(ATL_ST_RESETTING, &hw->state)) /* After reset, atl_fw_init() will apply the settings * skipped here */ goto unlock; if (changed & atl_thermal_monitor) ret = __atl_fw2_update_thermal(hw); else if (changed & atl_thermal_throttle && hw->link_state.thermal_throttled) __atl_fw2_set_link(hw); if (ret) /* __atl_fw2_update_thermal() failed. Revert flag * changes */ thermal->flags ^= changed; unlock: atl_unlock_fw(hw); return ret; } /* fw lock must be held */ static int __atl_fw2_get_hbeat(struct atl_hw *hw, uint16_t *hbeat) { int ret; uint32_t val; ret = atl_read_fwstat_word(hw, atl_fw2_stat_phy_hbeat, &val); if (ret) atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", -ret); else *hbeat = val & 0xffff; return ret; } static unsigned int atl_wdog_period = 1100; module_param_named(wdog_period, atl_wdog_period, uint, 0644); int atl_fw_init(struct atl_hw *hw) { uint32_t tries, reg, major; int ret; struct atl_mcp *mcp = &hw->mcp; tries = busy_wait(10000, mdelay(1), reg, atl_read(hw, 0x18), !reg); if (!reg) { Loading @@ -473,22 +796,87 @@ int atl_fw_init(struct atl_hw *hw) } if (major > 2) major--; hw->mcp.ops = &atl_fw_ops[major - 1]; hw->mcp.poll_link = major == 1; hw->mcp.fw_rev = reg; hw->mcp.fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); mcp->ops = &atl_fw_ops[major - 1]; mcp->fw_rev = reg; ret = mcp->ops->__wait_fw_init(hw); if (ret) return ret; mcp->fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); ret = __atl_fw2_get_hbeat(hw, &mcp->phy_hbeat); if (ret) return ret; mcp->next_wdog = jiffies + 2 * HZ; if (major > 1) { mcp->req_high = 0; ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_addr, &hw->mcp.fw_settings_addr); &mcp->fw_settings_addr); if (ret) return ret; ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_len, &hw->mcp.fw_settings_len); &mcp->fw_settings_len); if (ret) return ret; } return hw->mcp.ops->wait_fw_init(hw); ret = mcp->ops->__get_link_caps(hw); if (ret) return ret; if (!(mcp->caps_high & atl_fw2_set_thermal)) { if (hw->thermal.flags & atl_thermal_monitor) atl_dev_warn("Thermal monitoring not supported by firmware\n"); hw->thermal.flags &= ~(atl_thermal_monitor | atl_thermal_throttle); } else ret = __atl_fw2_update_thermal(hw); return ret; } void atl_fw_watchdog(struct atl_hw *hw) { struct atl_mcp *mcp = &hw->mcp; int ret; uint16_t hbeat; if (mcp->wdog_disabled || !time_after(jiffies, mcp->next_wdog)) return; if (test_bit(ATL_ST_RESETTING, &hw->state) || !test_bit(ATL_ST_ENABLED, &hw->state)) return; atl_lock_fw(hw); ret = __atl_fw2_get_hbeat(hw, &hbeat); if (ret) { atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", -ret); goto out; } if (hbeat == 0 && mcp->phy_hbeat == 0) { atl_dev_warn("FW heartbeat stuck at 0, probably not provisioned. Disabling watchdog.\n"); mcp->wdog_disabled = true; goto out; } if (hbeat == mcp->phy_hbeat) { atl_dev_err("FW watchdog: FW hang (PHY heartbeat stuck at %hd), resetting\n", hbeat); set_bit(ATL_ST_RESET_NEEDED, &hw->state); } mcp->phy_hbeat = hbeat; out: mcp->next_wdog = jiffies + atl_wdog_period * HZ / 1000; atl_unlock_fw(hw); } drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h +38 −2 Original line number Diff line number Diff line Loading @@ -12,6 +12,22 @@ struct atl_hw; struct atl_mcp { uint32_t fw_rev; struct atl_fw_ops *ops; uint32_t fw_stat_addr; uint32_t fw_settings_addr; uint32_t fw_settings_len; uint32_t req_high; uint32_t req_high_mask; /* Clears link rate-dependend bits */ uint32_t caps_low; uint32_t caps_high; struct mutex lock; unsigned long next_wdog; bool wdog_disabled; uint16_t phy_hbeat; }; struct atl_link_type { unsigned speed; unsigned ethtool_idx; Loading @@ -22,6 +38,13 @@ struct atl_link_type { extern struct atl_link_type atl_link_types[]; extern const int atl_num_rates; struct atl_fw2_thermal_cfg { uint32_t msg_id; uint8_t shutdown_temp; uint8_t high_temp; uint8_t normal_temp; }; #define atl_for_each_rate(idx, type) \ for (idx = 0, type = atl_link_types; \ idx < atl_num_rates; \ Loading @@ -37,12 +60,15 @@ enum atl_fw2_opts { atl_fw2_pause_mask = atl_fw2_pause | atl_fw2_asym_pause, atl_define_bit(atl_fw2_wake_on_link, 16) atl_define_bit(atl_fw2_phy_temp, 18) atl_define_bit(atl_fw2_set_thermal, 21) atl_define_bit(atl_fw2_link_drop, 22) atl_define_bit(atl_fw2_nic_proxy, 0x17) atl_define_bit(atl_fw2_wol, 0x18) atl_define_bit(atl_fw2_thermal_alarm, 29) }; enum atl_fw2_stat_offt { atl_fw2_stat_phy_hbeat = 0x4c, atl_fw2_stat_temp = 0x50, atl_fw2_stat_lcaps = 0x84, atl_fw2_stat_settings_addr = 0x110, Loading @@ -64,6 +90,12 @@ enum atl_fc_mode { atl_fc_full = atl_fc_rx | atl_fc_tx, }; enum atl_thermal_flags { atl_define_bit(atl_thermal_monitor, 0) atl_define_bit(atl_thermal_throttle, 1) atl_define_bit(atl_thermal_ignore_lims, 2) }; struct atl_fc_state { enum atl_fc_mode req; enum atl_fc_mode prev_req; Loading @@ -81,7 +113,11 @@ struct atl_link_state{ unsigned advertized; unsigned lp_advertized; unsigned prev_advertized; int lp_lowest; /* Idx of lowest rate advertized by * link partner in atl_link_types[] */ int throttled_to; /* Idx of the rate we're throttled to */ bool force_off; bool thermal_throttled; bool autoneg; bool eee; bool eee_enabled; Loading @@ -92,8 +128,8 @@ struct atl_link_state{ struct atl_fw_ops { void (*set_link)(struct atl_hw *hw, bool force); struct atl_link_type *(*check_link)(struct atl_hw *hw); int (*wait_fw_init)(struct atl_hw *hw); int (*get_link_caps)(struct atl_hw *hw); int (*__wait_fw_init)(struct atl_hw *hw); int (*__get_link_caps)(struct atl_hw *hw); int (*restart_aneg)(struct atl_hw *hw); void (*set_default_link)(struct atl_hw *hw); int (*enable_wol)(struct atl_hw *hw); Loading drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c +69 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h +41 −11 Original line number Diff line number Diff line Loading @@ -42,27 +42,45 @@ enum atl_board { ATL_AQC100, }; struct atl_thermal { unsigned flags; uint8_t crit; uint8_t high; uint8_t low; }; extern struct atl_thermal atl_def_thermal; enum atl_nic_state { ATL_ST_ENABLED, ATL_ST_CONFIGURED, ATL_ST_RINGS_RUNNING, /* ATL_ST_FWD_RINGS_RUNNING, */ ATL_ST_UP, ATL_ST_WORK_SCHED, ATL_ST_UPDATE_LINK, ATL_ST_RESETTING, ATL_ST_RESET_NEEDED, ATL_ST_GLOBAL_CONF_NEEDED, ATL_ST_START_NEEDED, ATL_ST_DETACHED, }; #define ATL_WAKE_SUPPORTED (WAKE_MAGIC | WAKE_PHY) struct atl_hw { uint8_t __iomem *regs; struct pci_dev *pdev; unsigned long state; struct atl_link_state link_state; unsigned wol_mode; struct { uint32_t fw_rev; bool poll_link; struct atl_fw_ops *ops; uint32_t fw_stat_addr; uint32_t fw_settings_addr; uint32_t fw_settings_len; struct mutex lock; } mcp; uint32_t intr_mask; struct atl_mcp mcp; uint32_t non_ring_intr_mask; uint8_t mac_addr[ETH_ALEN]; #define ATL_RSS_KEY_SIZE 40 uint8_t rss_key[ATL_RSS_KEY_SIZE]; #define ATL_RSS_TBL_SIZE (1 << 6) uint8_t rss_tbl[ATL_RSS_TBL_SIZE]; struct atl_thermal thermal; }; union atl_desc; Loading Loading @@ -178,7 +196,7 @@ static inline void atl_set_vlan_promisc(struct atl_hw *hw, int promisc) int atl_read_mcp_mem(struct atl_hw *hw, uint32_t mcp_addr, void *host_addr, unsigned size); int atl_hwinit(struct atl_nic *nic, enum atl_board brd_id); int atl_hwinit(struct atl_hw *hw, enum atl_board brd_id); void atl_refresh_link(struct atl_nic *nic); void atl_set_rss_key(struct atl_hw *hw); void atl_set_rss_tbl(struct atl_hw *hw); Loading Loading @@ -219,4 +237,16 @@ static inline int atl_read_fwsettings_word(struct atl_hw *hw, uint32_t offt, return atl_read_mcp_word(hw, offt + hw->mcp.fw_settings_addr, val); } static inline void atl_lock_fw(struct atl_hw *hw) { mutex_lock(&hw->mcp.lock); } static inline void atl_unlock_fw(struct atl_hw *hw) { mutex_unlock(&hw->mcp.lock); } void atl_fw_watchdog(struct atl_hw *hw); #endif Loading
drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +30 −15 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ #include <linux/netdevice.h> #include <linux/moduleparam.h> #define ATL_VERSION "1.0.19" #define ATL_VERSION "1.0.20" struct atl_nic; Loading Loading @@ -220,9 +220,8 @@ struct atl_nic { int nvecs; struct atl_hw hw; unsigned flags; unsigned long state; uint32_t priv_flags; struct timer_list link_timer; struct timer_list work_timer; int max_mtu; int requested_nvecs; int requested_rx_size; Loading @@ -249,13 +248,6 @@ enum atl_nic_flags { ATL_FL_WOL = BIT(1), }; enum atl_nic_state { ATL_ST_UP, ATL_ST_CONFIGURED, ATL_ST_ENABLED, ATL_ST_WORK_SCHED, }; #define ATL_PF(_name) ATL_PF_ ## _name #define ATL_PF_BIT(_name) ATL_PF_ ## _name ## _BIT #define ATL_DEF_PF_BIT(_name) ATL_PF_BIT(_name) = BIT(ATL_PF(_name)) Loading Loading @@ -335,18 +327,37 @@ extern int atl_enable_msi; #define atl_nic_err(fmt, args...) \ dev_err(&nic->hw.pdev->dev, fmt, ## args) #define atl_dev_init_warn(fmt, args...) \ do { \ if (hw) \ atl_dev_warn(fmt, ## args); \ else \ printk(KERN_WARNING "%s: " fmt, atl_driver_name, ##args); \ } while(0) #define atl_dev_init_err(fmt, args...) \ do { \ if (hw) \ atl_dev_warn(fmt, ## args); \ else \ printk(KERN_ERR "%s: " fmt, atl_driver_name, ##args); \ } while(0) #define atl_module_param(_name, _type, _mode) \ module_param_named(_name, atl_ ## _name, _type, _mode) static inline void atl_intr_enable_non_ring(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; uint32_t mask = hw->intr_mask; #ifdef CONFIG_ATLFWD_FWD mask |= (uint32_t)(nic->fwd.msi_map); #endif atl_intr_enable(hw, mask); atl_intr_enable(hw, hw->non_ring_intr_mask); } static inline void atl_intr_disable_non_ring(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; atl_intr_disable(hw, hw->non_ring_intr_mask); } netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev); Loading @@ -360,6 +371,7 @@ int atl_setup_datapath(struct atl_nic *nic); void atl_clear_datapath(struct atl_nic *nic); int atl_start_rings(struct atl_nic *nic); void atl_stop_rings(struct atl_nic *nic); void atl_clear_rdm_cache(struct atl_nic *nic); int atl_alloc_rings(struct atl_nic *nic); void atl_free_rings(struct atl_nic *nic); irqreturn_t atl_ring_irq(int irq, void *priv); Loading Loading @@ -403,5 +415,8 @@ int atl_mdio_write(struct atl_hw *hw, uint8_t prtad, uint8_t mmd, void atl_refresh_rxfs(struct atl_nic *nic); void atl_schedule_work(struct atl_nic *nic); int atl_hwmon_init(struct atl_nic *nic); int atl_update_thermal(struct atl_hw *hw); int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val); int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal); #endif
drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c +447 −59 Original line number Diff line number Diff line Loading @@ -36,17 +36,8 @@ struct atl_link_type atl_link_types[] = { const int atl_num_rates = ARRAY_SIZE(atl_link_types); static inline void atl_lock_fw(struct atl_hw *hw) { mutex_lock(&hw->mcp.lock); } static inline void atl_unlock_fw(struct atl_hw *hw) { mutex_unlock(&hw->mcp.lock); } static int atl_fw1_wait_fw_init(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw1_wait_fw_init(struct atl_hw *hw) { uint32_t hostData_addr; uint32_t id, new_id; Loading Loading @@ -79,7 +70,8 @@ static int atl_fw1_wait_fw_init(struct atl_hw *hw) return 0; } static int atl_fw2_wait_fw_init(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw2_wait_fw_init(struct atl_hw *hw) { uint32_t reg; Loading Loading @@ -148,6 +140,8 @@ static struct atl_link_type *atl_fw1_check_link(struct atl_hw *hw) return link; } static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts); static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) { struct atl_link_type *link; Loading @@ -162,58 +156,91 @@ static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) high = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)); link = atl_parse_fw_bits(hw, low, high, 1); if (!link) goto unlock; __atl_fw2_thermal_check(hw, low); /* Thermal check might have reset link due to throttling */ link = lstate->link; if (link) { if (high & atl_fw2_pause) fc |= atl_fc_rx; if (high & atl_fw2_asym_pause) fc |= atl_fc_tx; } lstate->fc.cur = fc; unlock: atl_unlock_fw(hw); return link; } static int atl_fw1_get_link_caps(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw1_get_link_caps(struct atl_hw *hw) { return 0; } static int atl_fw2_get_link_caps(struct atl_hw *hw) /* fw lock must be held */ static int __atl_fw2_get_link_caps(struct atl_hw *hw) { uint32_t fw_stat_addr = hw->mcp.fw_stat_addr; struct atl_mcp *mcp = &hw->mcp; uint32_t fw_stat_addr = mcp->fw_stat_addr; struct atl_link_type *rate; unsigned int supported = 0; uint32_t caps[2]; uint32_t caps[2], mask = atl_fw2_pause_mask | atl_fw2_link_drop; int i, ret; atl_lock_fw(hw); atl_dev_dbg("Host data struct addr: %#x\n", fw_stat_addr); ret = atl_read_mcp_mem(hw, fw_stat_addr + atl_fw2_stat_lcaps, caps, 8); if (ret) goto unlock; return ret; for (i = 0; i < atl_num_rates; i++) if (atl_link_types[i].fw_bits[1] & caps[0]) { mcp->caps_low = caps[0]; mcp->caps_high = caps[1]; atl_dev_dbg("Got link caps: %#x %#x\n", caps[0], caps[1]); atl_for_each_rate(i, rate) { uint32_t bit = rate->fw_bits[1]; if (bit & caps[0]) { supported |= BIT(i); if (atl_link_types[i].fw_bits[1] & caps[1]) if (bit & caps[1]) { supported |= BIT(i + ATL_EEE_BIT_OFFT); mask |= bit; } } } mcp->req_high_mask = ~mask; hw->link_state.supported = supported; hw->link_state.lp_lowest = fls(supported) - 1; unlock: atl_unlock_fw(hw); return ret; } static inline unsigned int atl_link_adv(struct atl_link_state *lstate) { return lstate->force_off ? 0 : lstate->advertized; struct atl_hw *hw = container_of(lstate, struct atl_hw, link_state); if (lstate->force_off) return 0; if (lstate->thermal_throttled && hw->thermal.flags & atl_thermal_throttle) /* FW doesn't provide raw LP's advertized rates, only * the rates adverized both by us and LP. Here we * advertize not just the throttled_to rate, but also * all the lower rates as well. That way if LP changes * or dynamically starts to adverize a lower rate than * throttled_to, we will notice that in * atl_fw2_thermal_check() and switch to that lower * rate there. */ return BIT(lstate->throttled_to + 1) - 1; return lstate->advertized; } static inline bool atl_fw1_set_link_needed(struct atl_link_state *lstate) Loading Loading @@ -276,16 +303,14 @@ static void atl_fw1_set_link(struct atl_hw *hw, bool force) atl_unlock_fw(hw); } static void atl_fw2_set_link(struct atl_hw *hw, bool force) /* fw lock must be held */ static void __atl_fw2_set_link(struct atl_hw *hw) { struct atl_link_state *lstate = &hw->link_state; uint32_t hi_bits = 0; uint32_t hi_bits; uint64_t bits; if (!force && !atl_fw2_set_link_needed(lstate)) return; atl_lock_fw(hw); hi_bits = hw->mcp.req_high & hw->mcp.req_high_mask; if (lstate->fc.req & atl_fc_rx) hi_bits |= atl_fw2_pause | atl_fw2_asym_pause; Loading @@ -294,15 +319,26 @@ static void atl_fw2_set_link(struct atl_hw *hw, bool force) hi_bits ^= atl_fw2_asym_pause; bits = atl_set_fw_bits(hw, 1); hi_bits |= bits >> 32; /* If no modes are advertized, put PHY into low-power */ if (!bits) hi_bits = atl_fw2_link_drop; hi_bits |= atl_fw2_link_drop; else hi_bits |= bits >> 32; hw->mcp.req_high = hi_bits; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_LOW), bits); atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), hi_bits); } static void atl_fw2_set_link(struct atl_hw *hw, bool force) { if (!force && !atl_fw2_set_link_needed(&hw->link_state)) return; atl_lock_fw(hw); __atl_fw2_set_link(hw); atl_unlock_fw(hw); } Loading @@ -314,6 +350,8 @@ static int atl_fw1_unsupported(struct atl_hw *hw) static int atl_fw2_restart_aneg(struct atl_hw *hw) { atl_lock_fw(hw); /* Autoneg restart is self-clearing, no need to track via * mcp->req_high */ atl_set_bits(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), BIT(31)); atl_unlock_fw(hw); return 0; Loading Loading @@ -398,15 +436,17 @@ int atl_read_mcp_word(struct atl_hw *hw, uint32_t offt, uint32_t *val) return 0; } static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) /* fw lock must be held */ static int __atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) { uint32_t req, res; int ret = 0; atl_lock_fw(hw); if (test_bit(ATL_ST_RESETTING, &hw->state)) return 0; req = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH)); req ^= atl_fw2_phy_temp; hw->mcp.req_high ^= atl_fw2_phy_temp; req = hw->mcp.req_high; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), req); busy_wait(1000, udelay(10), res, Loading @@ -414,27 +454,95 @@ static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) ((res ^ req) & atl_fw2_phy_temp) != 0); if (((res ^ req) & atl_fw2_phy_temp) != 0) { atl_dev_err("Timeout waiting for PHY temperature\n"); ret = -EIO; goto unlock; return -EIO; } ret = atl_read_fwstat_word(hw, atl_fw2_stat_temp, &res); if (ret) goto unlock; return ret; *temp = (res & 0xffff) * 1000 / 256; unlock: return ret; } static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) { int ret; atl_lock_fw(hw); ret = __atl_fw2_get_phy_temperature(hw, temp); atl_unlock_fw(hw); return ret; } /* fw lock must be held */ static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts) { bool alarm; int temp, ret; struct atl_link_state *lstate = &hw->link_state; struct atl_link_type *link = lstate->link; int lowest; alarm = !!(sts & atl_fw2_thermal_alarm); if (link) { /* ffs() / fls() number bits starting at 1 */ lowest = ffs(lstate->lp_advertized) - 1; if (lowest < lstate->lp_lowest) { lstate->lp_lowest = lowest; if (lowest < lstate->throttled_to && lstate->thermal_throttled && alarm) /* We're still thermal-throttled, and * just found out we can lower the * speed even more, so renegotiate. */ goto relink; } } else lstate->lp_lowest = fls(lstate->supported) - 1; if (alarm == lstate->thermal_throttled) return; lstate->thermal_throttled = alarm; ret = __atl_fw2_get_phy_temperature(hw, &temp); if (ret) temp = 0; else /* Temperature is in millidegrees C */ temp = (temp + 50) / 100; if (alarm) { if (temp) atl_dev_warn("PHY temperature above threshold: %d.%d\n", temp / 10, temp % 10); else atl_dev_warn("PHY temperature above threshold\n"); } else { if (temp) atl_dev_warn("PHY temperature back in range: %d.%d\n", temp / 10, temp % 10); else atl_dev_warn("PHY temperature back in range\n"); } relink: if (hw->thermal.flags & atl_thermal_throttle) { /* If throttling is enabled, renegotiate link */ lstate->link = 0; lstate->throttled_to = lstate->lp_lowest; __atl_fw2_set_link(hw); } } static struct atl_fw_ops atl_fw_ops[2] = { [0] = { .wait_fw_init = atl_fw1_wait_fw_init, .__wait_fw_init = __atl_fw1_wait_fw_init, .set_link = atl_fw1_set_link, .check_link = atl_fw1_check_link, .get_link_caps = atl_fw1_get_link_caps, .__get_link_caps = __atl_fw1_get_link_caps, .restart_aneg = atl_fw1_unsupported, .set_default_link = atl_fw1_set_default_link, .enable_wol = atl_fw1_unsupported, Loading @@ -442,10 +550,10 @@ static struct atl_fw_ops atl_fw_ops[2] = { .efuse_shadow_addr_reg = ATL_MCP_SCRATCH(FW1_EFUSE_SHADOW), }, [1] = { .wait_fw_init = atl_fw2_wait_fw_init, .__wait_fw_init = __atl_fw2_wait_fw_init, .set_link = atl_fw2_set_link, .check_link = atl_fw2_check_link, .get_link_caps = atl_fw2_get_link_caps, .__get_link_caps = __atl_fw2_get_link_caps, .restart_aneg = atl_fw2_restart_aneg, .set_default_link = atl_fw2_set_default_link, .enable_wol = atl_fw2_enable_wol, Loading @@ -454,10 +562,225 @@ static struct atl_fw_ops atl_fw_ops[2] = { }, }; /* fw lock must be held */ static int __atl_fw2_set_thermal_monitor(struct atl_hw *hw, bool enable) { struct atl_mcp *mcp = &hw->mcp; int ret; uint32_t high; if (enable) { struct atl_fw2_thermal_cfg cfg __attribute__((__aligned__(4))); cfg.msg_id = 0x17; cfg.shutdown_temp = hw->thermal.crit; cfg.high_temp = hw->thermal.high; cfg.normal_temp = hw->thermal.low; ret = atl_write_mcp_mem(hw, 0, &cfg, (sizeof(cfg) + 3) & ~3, MCP_AREA_CONFIG); if (ret) { atl_dev_err("Failed to upload thermal thresholds to firmware: %d\n", ret); return ret; } mcp->req_high |= atl_fw2_set_thermal; } else mcp->req_high &= ~atl_fw2_set_thermal; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), mcp->req_high); busy_wait(1000, udelay(10), high, atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)), !!(high & atl_fw2_set_thermal) != enable); if (!!(high & atl_fw2_set_thermal) != enable) { atl_dev_err("Timeout waiting for thermal monitoring FW request\n"); return -EIO; } return 0; } /* fw lock must be held */ static int __atl_fw2_update_thermal(struct atl_hw *hw) { struct atl_mcp *mcp = &hw->mcp; int ret = 0; bool enable = !!(hw->thermal.flags & atl_thermal_monitor); if (!enable || (mcp->req_high & atl_fw2_set_thermal)) { /* If monitoring is on and we need to change the * thresholds, we need to temporarily disable thermal * monitoring first. */ ret = __atl_fw2_set_thermal_monitor(hw, false); if (ret) return ret; } if (enable) ret = __atl_fw2_set_thermal_monitor(hw, true); /* Thresholds might have changed, recheck state. */ __atl_fw2_thermal_check(hw, atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_LOW))); return ret; } struct atl_thermal_limit { uintptr_t offset; const char *name; unsigned min; unsigned max; }; #define atl_def_thermal_limit(_name, _field, _min, _max) \ { \ .offset = offsetof(struct atl_thermal, _field), \ .name = _name, \ .min = _min, \ .max = _max, \ }, static struct atl_thermal_limit atl_thermal_limits[] = { atl_def_thermal_limit("Shutdown", crit, 108, 118) atl_def_thermal_limit("High", high, 90, 107) atl_def_thermal_limit("Normal", low, 50, 85) }; int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal) { int i; bool ignore = !!(thermal->flags & atl_thermal_ignore_lims); for (i = 0; i < ARRAY_SIZE(atl_thermal_limits); i++) { struct atl_thermal_limit *lim = &atl_thermal_limits[i]; unsigned val = *((uint8_t *)thermal + lim->offset); if (val >= lim->min && val <= lim->max) continue; if (ignore) { atl_dev_init_warn("%s temperature threshold out of range (%d - %d): %d, allowing anyway\n", lim->name, lim->min, lim->max, val); continue; } else { atl_dev_init_err("%s temperature threshold out of range (%d - %d): %d\n", lim->name, lim->min, lim->max, val); return -EINVAL; } } return 0; } int atl_update_thermal(struct atl_hw *hw) { int ret; ret = atl_verify_thermal_limits(hw, &hw->thermal); if (ret) return ret; if (test_bit(ATL_ST_RESETTING, &hw->state)) /* After reset, atl_fw_init() will apply the settings * skipped here */ return 0; atl_lock_fw(hw); ret = __atl_fw2_update_thermal(hw); atl_unlock_fw(hw); return ret; } int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val) { struct atl_thermal *thermal = &hw->thermal; unsigned flags, changed; int ret = 0; atl_lock_fw(hw); flags = thermal->flags; switch (bit) { case atl_thermal_monitor_shift: if (!val) /* Disable throttling along with monitoring */ flags &= ~atl_thermal_throttle; else if (!(hw->mcp.caps_high & atl_fw2_set_thermal)) { atl_dev_err("Thermal monitoring not supported by firmware\n"); return -EINVAL; } break; case atl_thermal_throttle_shift: if (val && !(flags & atl_thermal_monitor)) { atl_dev_err("Thermal monitoring needs to be enabled before enabling throttling\n"); ret = -EINVAL; } break; case atl_thermal_ignore_lims_shift: break; default: ret = -EINVAL; break; } if (ret) goto unlock; flags &= ~BIT(bit); flags |= val << bit; changed = flags ^ thermal->flags; thermal->flags = flags; if (test_bit(ATL_ST_RESETTING, &hw->state)) /* After reset, atl_fw_init() will apply the settings * skipped here */ goto unlock; if (changed & atl_thermal_monitor) ret = __atl_fw2_update_thermal(hw); else if (changed & atl_thermal_throttle && hw->link_state.thermal_throttled) __atl_fw2_set_link(hw); if (ret) /* __atl_fw2_update_thermal() failed. Revert flag * changes */ thermal->flags ^= changed; unlock: atl_unlock_fw(hw); return ret; } /* fw lock must be held */ static int __atl_fw2_get_hbeat(struct atl_hw *hw, uint16_t *hbeat) { int ret; uint32_t val; ret = atl_read_fwstat_word(hw, atl_fw2_stat_phy_hbeat, &val); if (ret) atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", -ret); else *hbeat = val & 0xffff; return ret; } static unsigned int atl_wdog_period = 1100; module_param_named(wdog_period, atl_wdog_period, uint, 0644); int atl_fw_init(struct atl_hw *hw) { uint32_t tries, reg, major; int ret; struct atl_mcp *mcp = &hw->mcp; tries = busy_wait(10000, mdelay(1), reg, atl_read(hw, 0x18), !reg); if (!reg) { Loading @@ -473,22 +796,87 @@ int atl_fw_init(struct atl_hw *hw) } if (major > 2) major--; hw->mcp.ops = &atl_fw_ops[major - 1]; hw->mcp.poll_link = major == 1; hw->mcp.fw_rev = reg; hw->mcp.fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); mcp->ops = &atl_fw_ops[major - 1]; mcp->fw_rev = reg; ret = mcp->ops->__wait_fw_init(hw); if (ret) return ret; mcp->fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); ret = __atl_fw2_get_hbeat(hw, &mcp->phy_hbeat); if (ret) return ret; mcp->next_wdog = jiffies + 2 * HZ; if (major > 1) { mcp->req_high = 0; ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_addr, &hw->mcp.fw_settings_addr); &mcp->fw_settings_addr); if (ret) return ret; ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_len, &hw->mcp.fw_settings_len); &mcp->fw_settings_len); if (ret) return ret; } return hw->mcp.ops->wait_fw_init(hw); ret = mcp->ops->__get_link_caps(hw); if (ret) return ret; if (!(mcp->caps_high & atl_fw2_set_thermal)) { if (hw->thermal.flags & atl_thermal_monitor) atl_dev_warn("Thermal monitoring not supported by firmware\n"); hw->thermal.flags &= ~(atl_thermal_monitor | atl_thermal_throttle); } else ret = __atl_fw2_update_thermal(hw); return ret; } void atl_fw_watchdog(struct atl_hw *hw) { struct atl_mcp *mcp = &hw->mcp; int ret; uint16_t hbeat; if (mcp->wdog_disabled || !time_after(jiffies, mcp->next_wdog)) return; if (test_bit(ATL_ST_RESETTING, &hw->state) || !test_bit(ATL_ST_ENABLED, &hw->state)) return; atl_lock_fw(hw); ret = __atl_fw2_get_hbeat(hw, &hbeat); if (ret) { atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", -ret); goto out; } if (hbeat == 0 && mcp->phy_hbeat == 0) { atl_dev_warn("FW heartbeat stuck at 0, probably not provisioned. Disabling watchdog.\n"); mcp->wdog_disabled = true; goto out; } if (hbeat == mcp->phy_hbeat) { atl_dev_err("FW watchdog: FW hang (PHY heartbeat stuck at %hd), resetting\n", hbeat); set_bit(ATL_ST_RESET_NEEDED, &hw->state); } mcp->phy_hbeat = hbeat; out: mcp->next_wdog = jiffies + atl_wdog_period * HZ / 1000; atl_unlock_fw(hw); }
drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h +38 −2 Original line number Diff line number Diff line Loading @@ -12,6 +12,22 @@ struct atl_hw; struct atl_mcp { uint32_t fw_rev; struct atl_fw_ops *ops; uint32_t fw_stat_addr; uint32_t fw_settings_addr; uint32_t fw_settings_len; uint32_t req_high; uint32_t req_high_mask; /* Clears link rate-dependend bits */ uint32_t caps_low; uint32_t caps_high; struct mutex lock; unsigned long next_wdog; bool wdog_disabled; uint16_t phy_hbeat; }; struct atl_link_type { unsigned speed; unsigned ethtool_idx; Loading @@ -22,6 +38,13 @@ struct atl_link_type { extern struct atl_link_type atl_link_types[]; extern const int atl_num_rates; struct atl_fw2_thermal_cfg { uint32_t msg_id; uint8_t shutdown_temp; uint8_t high_temp; uint8_t normal_temp; }; #define atl_for_each_rate(idx, type) \ for (idx = 0, type = atl_link_types; \ idx < atl_num_rates; \ Loading @@ -37,12 +60,15 @@ enum atl_fw2_opts { atl_fw2_pause_mask = atl_fw2_pause | atl_fw2_asym_pause, atl_define_bit(atl_fw2_wake_on_link, 16) atl_define_bit(atl_fw2_phy_temp, 18) atl_define_bit(atl_fw2_set_thermal, 21) atl_define_bit(atl_fw2_link_drop, 22) atl_define_bit(atl_fw2_nic_proxy, 0x17) atl_define_bit(atl_fw2_wol, 0x18) atl_define_bit(atl_fw2_thermal_alarm, 29) }; enum atl_fw2_stat_offt { atl_fw2_stat_phy_hbeat = 0x4c, atl_fw2_stat_temp = 0x50, atl_fw2_stat_lcaps = 0x84, atl_fw2_stat_settings_addr = 0x110, Loading @@ -64,6 +90,12 @@ enum atl_fc_mode { atl_fc_full = atl_fc_rx | atl_fc_tx, }; enum atl_thermal_flags { atl_define_bit(atl_thermal_monitor, 0) atl_define_bit(atl_thermal_throttle, 1) atl_define_bit(atl_thermal_ignore_lims, 2) }; struct atl_fc_state { enum atl_fc_mode req; enum atl_fc_mode prev_req; Loading @@ -81,7 +113,11 @@ struct atl_link_state{ unsigned advertized; unsigned lp_advertized; unsigned prev_advertized; int lp_lowest; /* Idx of lowest rate advertized by * link partner in atl_link_types[] */ int throttled_to; /* Idx of the rate we're throttled to */ bool force_off; bool thermal_throttled; bool autoneg; bool eee; bool eee_enabled; Loading @@ -92,8 +128,8 @@ struct atl_link_state{ struct atl_fw_ops { void (*set_link)(struct atl_hw *hw, bool force); struct atl_link_type *(*check_link)(struct atl_hw *hw); int (*wait_fw_init)(struct atl_hw *hw); int (*get_link_caps)(struct atl_hw *hw); int (*__wait_fw_init)(struct atl_hw *hw); int (*__get_link_caps)(struct atl_hw *hw); int (*restart_aneg)(struct atl_hw *hw); void (*set_default_link)(struct atl_hw *hw); int (*enable_wol)(struct atl_hw *hw); Loading
drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c +69 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes
drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h +41 −11 Original line number Diff line number Diff line Loading @@ -42,27 +42,45 @@ enum atl_board { ATL_AQC100, }; struct atl_thermal { unsigned flags; uint8_t crit; uint8_t high; uint8_t low; }; extern struct atl_thermal atl_def_thermal; enum atl_nic_state { ATL_ST_ENABLED, ATL_ST_CONFIGURED, ATL_ST_RINGS_RUNNING, /* ATL_ST_FWD_RINGS_RUNNING, */ ATL_ST_UP, ATL_ST_WORK_SCHED, ATL_ST_UPDATE_LINK, ATL_ST_RESETTING, ATL_ST_RESET_NEEDED, ATL_ST_GLOBAL_CONF_NEEDED, ATL_ST_START_NEEDED, ATL_ST_DETACHED, }; #define ATL_WAKE_SUPPORTED (WAKE_MAGIC | WAKE_PHY) struct atl_hw { uint8_t __iomem *regs; struct pci_dev *pdev; unsigned long state; struct atl_link_state link_state; unsigned wol_mode; struct { uint32_t fw_rev; bool poll_link; struct atl_fw_ops *ops; uint32_t fw_stat_addr; uint32_t fw_settings_addr; uint32_t fw_settings_len; struct mutex lock; } mcp; uint32_t intr_mask; struct atl_mcp mcp; uint32_t non_ring_intr_mask; uint8_t mac_addr[ETH_ALEN]; #define ATL_RSS_KEY_SIZE 40 uint8_t rss_key[ATL_RSS_KEY_SIZE]; #define ATL_RSS_TBL_SIZE (1 << 6) uint8_t rss_tbl[ATL_RSS_TBL_SIZE]; struct atl_thermal thermal; }; union atl_desc; Loading Loading @@ -178,7 +196,7 @@ static inline void atl_set_vlan_promisc(struct atl_hw *hw, int promisc) int atl_read_mcp_mem(struct atl_hw *hw, uint32_t mcp_addr, void *host_addr, unsigned size); int atl_hwinit(struct atl_nic *nic, enum atl_board brd_id); int atl_hwinit(struct atl_hw *hw, enum atl_board brd_id); void atl_refresh_link(struct atl_nic *nic); void atl_set_rss_key(struct atl_hw *hw); void atl_set_rss_tbl(struct atl_hw *hw); Loading Loading @@ -219,4 +237,16 @@ static inline int atl_read_fwsettings_word(struct atl_hw *hw, uint32_t offt, return atl_read_mcp_word(hw, offt + hw->mcp.fw_settings_addr, val); } static inline void atl_lock_fw(struct atl_hw *hw) { mutex_lock(&hw->mcp.lock); } static inline void atl_unlock_fw(struct atl_hw *hw) { mutex_unlock(&hw->mcp.lock); } void atl_fw_watchdog(struct atl_hw *hw); #endif