Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 233704e3 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "atlantic-forwarding driver v1.0.20"

parents 2e7f5378 71911a29
Loading
Loading
Loading
Loading
+30 −15
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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))
@@ -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);
@@ -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);
@@ -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
+447 −59
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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;
@@ -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)
@@ -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;
@@ -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);
}

@@ -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;
@@ -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,
@@ -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,
@@ -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,
@@ -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) {
@@ -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);
}
+38 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;		\
@@ -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,
@@ -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;
@@ -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;
@@ -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);
+69 −24

File changed.

Preview size limit exceeded, changes collapsed.

+41 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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