1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3 
4 #include <linux/mlx5/driver.h>
5 #include "lib/tout.h"
6 
7 struct mlx5_timeouts {
8 	u64 to[MAX_TIMEOUT_TYPES];
9 };
10 
11 static const u32 tout_def_sw_val[MAX_TIMEOUT_TYPES] = {
12 	[MLX5_TO_FW_PRE_INIT_TIMEOUT_MS] = 120000,
13 	[MLX5_TO_FW_PRE_INIT_ON_RECOVERY_TIMEOUT_MS] = 7200000,
14 	[MLX5_TO_FW_PRE_INIT_WARN_MESSAGE_INTERVAL_MS] = 20000,
15 	[MLX5_TO_FW_PRE_INIT_WAIT_MS] = 2,
16 	[MLX5_TO_FW_INIT_MS] = 2000,
17 	[MLX5_TO_CMD_MS] = 60000,
18 	[MLX5_TO_PCI_TOGGLE_MS] =  2000,
19 	[MLX5_TO_HEALTH_POLL_INTERVAL_MS] =  2000,
20 	[MLX5_TO_FULL_CRDUMP_MS] = 60000,
21 	[MLX5_TO_FW_RESET_MS] = 60000,
22 	[MLX5_TO_FLUSH_ON_ERROR_MS] = 2000,
23 	[MLX5_TO_PCI_SYNC_UPDATE_MS] = 5000,
24 	[MLX5_TO_TEARDOWN_MS] = 3000,
25 	[MLX5_TO_FSM_REACTIVATE_MS] = 5000,
26 	[MLX5_TO_RECLAIM_PAGES_MS] = 5000,
27 	[MLX5_TO_RECLAIM_VFS_PAGES_MS] = 120000
28 };
29 
tout_set(struct mlx5_core_dev * dev,u64 val,enum mlx5_timeouts_types type)30 static void tout_set(struct mlx5_core_dev *dev, u64 val, enum mlx5_timeouts_types type)
31 {
32 	dev->timeouts->to[type] = val;
33 }
34 
mlx5_tout_init(struct mlx5_core_dev * dev)35 int mlx5_tout_init(struct mlx5_core_dev *dev)
36 {
37 	int i;
38 
39 	dev->timeouts = kmalloc(sizeof(*dev->timeouts), GFP_KERNEL);
40 	if (!dev->timeouts)
41 		return -ENOMEM;
42 
43 	for (i = 0; i < MAX_TIMEOUT_TYPES; i++)
44 		tout_set(dev, tout_def_sw_val[i], i);
45 
46 	return 0;
47 }
48 
mlx5_tout_cleanup(struct mlx5_core_dev * dev)49 void mlx5_tout_cleanup(struct mlx5_core_dev *dev)
50 {
51 	kfree(dev->timeouts);
52 }
53 
54 /* Time register consists of two fields to_multiplier(time out multiplier)
55  * and to_value(time out value). to_value is the quantity of the time units and
56  * to_multiplier is the type and should be one off these four values.
57  * 0x0: millisecond
58  * 0x1: seconds
59  * 0x2: minutes
60  * 0x3: hours
61  * this function converts the time stored in the two register fields into
62  * millisecond.
63  */
tout_convert_reg_field_to_ms(u32 to_mul,u32 to_val)64 static u64 tout_convert_reg_field_to_ms(u32 to_mul, u32 to_val)
65 {
66 	u64 msec = to_val;
67 
68 	to_mul &= 0x3;
69 	/* convert hours/minutes/seconds to miliseconds */
70 	if (to_mul)
71 		msec *= 1000 * int_pow(60, to_mul - 1);
72 
73 	return msec;
74 }
75 
tout_convert_iseg_to_ms(u32 iseg_to)76 static u64 tout_convert_iseg_to_ms(u32 iseg_to)
77 {
78 	return tout_convert_reg_field_to_ms(iseg_to >> 29, iseg_to & 0xfffff);
79 }
80 
tout_is_supported(struct mlx5_core_dev * dev)81 static bool tout_is_supported(struct mlx5_core_dev *dev)
82 {
83 	return !!ioread32be(&dev->iseg->cmd_q_init_to);
84 }
85 
mlx5_tout_query_iseg(struct mlx5_core_dev * dev)86 void mlx5_tout_query_iseg(struct mlx5_core_dev *dev)
87 {
88 	u32 to;
89 
90 	if (!tout_is_supported(dev))
91 		return;
92 
93 	to = ioread32be(&dev->iseg->cmd_q_init_to);
94 	tout_set(dev, tout_convert_iseg_to_ms(to), MLX5_TO_FW_INIT_MS);
95 
96 	to = ioread32be(&dev->iseg->cmd_exec_to);
97 	tout_set(dev, tout_convert_iseg_to_ms(to), MLX5_TO_CMD_MS);
98 }
99 
_mlx5_tout_ms(struct mlx5_core_dev * dev,enum mlx5_timeouts_types type)100 u64 _mlx5_tout_ms(struct mlx5_core_dev *dev, enum mlx5_timeouts_types type)
101 {
102 	return dev->timeouts->to[type];
103 }
104 
105 #define MLX5_TIMEOUT_QUERY(fld, reg_out) \
106 	({ \
107 	struct mlx5_ifc_default_timeout_bits *time_field; \
108 	u32 to_multi, to_value; \
109 	u64 to_val_ms; \
110 	\
111 	time_field = MLX5_ADDR_OF(dtor_reg, reg_out, fld); \
112 	to_multi = MLX5_GET(default_timeout, time_field, to_multiplier); \
113 	to_value = MLX5_GET(default_timeout, time_field, to_value); \
114 	to_val_ms = tout_convert_reg_field_to_ms(to_multi, to_value); \
115 	to_val_ms; \
116 	})
117 
118 #define MLX5_TIMEOUT_FILL(fld, reg_out, dev, to_type, to_extra) \
119 	({ \
120 	u64 fw_to = MLX5_TIMEOUT_QUERY(fld, reg_out); \
121 	tout_set(dev, fw_to + (to_extra), to_type); \
122 	fw_to; \
123 	})
124 
tout_query_dtor(struct mlx5_core_dev * dev)125 static int tout_query_dtor(struct mlx5_core_dev *dev)
126 {
127 	u64 pcie_toggle_to_val, tear_down_to_val;
128 	u32 out[MLX5_ST_SZ_DW(dtor_reg)] = {};
129 	u32 in[MLX5_ST_SZ_DW(dtor_reg)] = {};
130 	int err;
131 
132 	err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), MLX5_REG_DTOR, 0, 0);
133 	if (err)
134 		return err;
135 
136 	pcie_toggle_to_val = MLX5_TIMEOUT_FILL(pcie_toggle_to, out, dev, MLX5_TO_PCI_TOGGLE_MS, 0);
137 	MLX5_TIMEOUT_FILL(fw_reset_to, out, dev, MLX5_TO_FW_RESET_MS, pcie_toggle_to_val);
138 
139 	tear_down_to_val = MLX5_TIMEOUT_FILL(tear_down_to, out, dev, MLX5_TO_TEARDOWN_MS, 0);
140 	MLX5_TIMEOUT_FILL(pci_sync_update_to, out, dev, MLX5_TO_PCI_SYNC_UPDATE_MS,
141 			  tear_down_to_val);
142 
143 	MLX5_TIMEOUT_FILL(health_poll_to, out, dev, MLX5_TO_HEALTH_POLL_INTERVAL_MS, 0);
144 	MLX5_TIMEOUT_FILL(full_crdump_to, out, dev, MLX5_TO_FULL_CRDUMP_MS, 0);
145 	MLX5_TIMEOUT_FILL(flush_on_err_to, out, dev, MLX5_TO_FLUSH_ON_ERROR_MS, 0);
146 	MLX5_TIMEOUT_FILL(fsm_reactivate_to, out, dev, MLX5_TO_FSM_REACTIVATE_MS, 0);
147 	MLX5_TIMEOUT_FILL(reclaim_pages_to, out, dev, MLX5_TO_RECLAIM_PAGES_MS, 0);
148 	MLX5_TIMEOUT_FILL(reclaim_vfs_pages_to, out, dev, MLX5_TO_RECLAIM_VFS_PAGES_MS, 0);
149 
150 	return 0;
151 }
152 
mlx5_tout_query_dtor(struct mlx5_core_dev * dev)153 int mlx5_tout_query_dtor(struct mlx5_core_dev *dev)
154 {
155 	if (tout_is_supported(dev))
156 		return tout_query_dtor(dev);
157 
158 	return 0;
159 }
160