1 /* 2 * drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c 3 * Copyright (c) 2015 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the names of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * Alternatively, this software may be distributed under the terms of the 19 * GNU General Public License ("GPL") version 2 as published by the Free 20 * Software Foundation. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <linux/kernel.h> 36 #include <linux/types.h> 37 #include <linux/device.h> 38 #include <linux/sysfs.h> 39 #include <linux/hwmon.h> 40 #include <linux/err.h> 41 42 #include "core.h" 43 44 #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127 45 #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \ 46 MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX) 47 48 struct mlxsw_hwmon_attr { 49 struct device_attribute dev_attr; 50 struct mlxsw_hwmon *hwmon; 51 unsigned int type_index; 52 char name[32]; 53 }; 54 55 struct mlxsw_hwmon { 56 struct mlxsw_core *core; 57 const struct mlxsw_bus_info *bus_info; 58 struct device *hwmon_dev; 59 struct attribute_group group; 60 const struct attribute_group *groups[2]; 61 struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1]; 62 struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT]; 63 unsigned int attrs_count; 64 }; 65 66 static ssize_t mlxsw_hwmon_temp_show(struct device *dev, 67 struct device_attribute *attr, 68 char *buf) 69 { 70 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 71 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 72 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 73 char mtmp_pl[MLXSW_REG_MTMP_LEN]; 74 unsigned int temp; 75 int err; 76 77 mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, 78 false, false); 79 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); 80 if (err) { 81 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n"); 82 return err; 83 } 84 mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL); 85 return sprintf(buf, "%u\n", temp); 86 } 87 88 static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, 89 struct device_attribute *attr, 90 char *buf) 91 { 92 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 93 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 94 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 95 char mtmp_pl[MLXSW_REG_MTMP_LEN]; 96 unsigned int temp_max; 97 int err; 98 99 mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, 100 false, false); 101 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); 102 if (err) { 103 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n"); 104 return err; 105 } 106 mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL); 107 return sprintf(buf, "%u\n", temp_max); 108 } 109 110 static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, 111 struct device_attribute *attr, 112 const char *buf, size_t len) 113 { 114 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 115 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 116 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 117 char mtmp_pl[MLXSW_REG_MTMP_LEN]; 118 unsigned long val; 119 int err; 120 121 err = kstrtoul(buf, 10, &val); 122 if (err) 123 return err; 124 if (val != 1) 125 return -EINVAL; 126 127 mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true); 128 err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); 129 if (err) { 130 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n"); 131 return err; 132 } 133 return len; 134 } 135 136 static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, 137 struct device_attribute *attr, 138 char *buf) 139 { 140 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 141 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 142 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 143 char mfsm_pl[MLXSW_REG_MFSM_LEN]; 144 int err; 145 146 mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index); 147 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl); 148 if (err) { 149 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n"); 150 return err; 151 } 152 return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl)); 153 } 154 155 static ssize_t mlxsw_hwmon_pwm_show(struct device *dev, 156 struct device_attribute *attr, 157 char *buf) 158 { 159 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 160 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 161 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 162 char mfsc_pl[MLXSW_REG_MFSC_LEN]; 163 int err; 164 165 mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0); 166 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); 167 if (err) { 168 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n"); 169 return err; 170 } 171 return sprintf(buf, "%u\n", 172 mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl)); 173 } 174 175 static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, 176 struct device_attribute *attr, 177 const char *buf, size_t len) 178 { 179 struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = 180 container_of(attr, struct mlxsw_hwmon_attr, dev_attr); 181 struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; 182 char mfsc_pl[MLXSW_REG_MFSC_LEN]; 183 unsigned long val; 184 int err; 185 186 err = kstrtoul(buf, 10, &val); 187 if (err) 188 return err; 189 if (val > 255) 190 return -EINVAL; 191 192 mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val); 193 err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); 194 if (err) { 195 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n"); 196 return err; 197 } 198 return len; 199 } 200 201 enum mlxsw_hwmon_attr_type { 202 MLXSW_HWMON_ATTR_TYPE_TEMP, 203 MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, 204 MLXSW_HWMON_ATTR_TYPE_TEMP_RST, 205 MLXSW_HWMON_ATTR_TYPE_FAN_RPM, 206 MLXSW_HWMON_ATTR_TYPE_PWM, 207 }; 208 209 static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, 210 enum mlxsw_hwmon_attr_type attr_type, 211 unsigned int type_index, unsigned int num) { 212 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr; 213 unsigned int attr_index; 214 215 attr_index = mlxsw_hwmon->attrs_count; 216 mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index]; 217 218 switch (attr_type) { 219 case MLXSW_HWMON_ATTR_TYPE_TEMP: 220 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show; 221 mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; 222 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), 223 "temp%u_input", num + 1); 224 break; 225 case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX: 226 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show; 227 mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; 228 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), 229 "temp%u_highest", num + 1); 230 break; 231 case MLXSW_HWMON_ATTR_TYPE_TEMP_RST: 232 mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store; 233 mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR; 234 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), 235 "temp%u_reset_history", num + 1); 236 break; 237 case MLXSW_HWMON_ATTR_TYPE_FAN_RPM: 238 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show; 239 mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; 240 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), 241 "fan%u_input", num + 1); 242 break; 243 case MLXSW_HWMON_ATTR_TYPE_PWM: 244 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show; 245 mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store; 246 mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO; 247 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), 248 "pwm%u", num + 1); 249 break; 250 default: 251 WARN_ON(1); 252 } 253 254 mlxsw_hwmon_attr->type_index = type_index; 255 mlxsw_hwmon_attr->hwmon = mlxsw_hwmon; 256 mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name; 257 sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr); 258 259 mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr; 260 mlxsw_hwmon->attrs_count++; 261 } 262 263 static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) 264 { 265 char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0}; 266 char mtmp_pl[MLXSW_REG_MTMP_LEN]; 267 u8 sensor_count; 268 int i; 269 int err; 270 271 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl); 272 if (err) { 273 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n"); 274 return err; 275 } 276 sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl); 277 for (i = 0; i < sensor_count; i++) { 278 mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true); 279 err = mlxsw_reg_write(mlxsw_hwmon->core, 280 MLXSW_REG(mtmp), mtmp_pl); 281 if (err) { 282 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n", 283 i); 284 return err; 285 } 286 mlxsw_hwmon_attr_add(mlxsw_hwmon, 287 MLXSW_HWMON_ATTR_TYPE_TEMP, i, i); 288 mlxsw_hwmon_attr_add(mlxsw_hwmon, 289 MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i); 290 mlxsw_hwmon_attr_add(mlxsw_hwmon, 291 MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i); 292 } 293 return 0; 294 } 295 296 static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon) 297 { 298 char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0}; 299 enum mlxsw_reg_mfcr_pwm_frequency freq; 300 unsigned int type_index; 301 unsigned int num; 302 u16 tacho_active; 303 u8 pwm_active; 304 int err; 305 306 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl); 307 if (err) { 308 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n"); 309 return err; 310 } 311 mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active); 312 num = 0; 313 for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) { 314 if (tacho_active & BIT(type_index)) 315 mlxsw_hwmon_attr_add(mlxsw_hwmon, 316 MLXSW_HWMON_ATTR_TYPE_FAN_RPM, 317 type_index, num++); 318 } 319 num = 0; 320 for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) { 321 if (pwm_active & BIT(type_index)) 322 mlxsw_hwmon_attr_add(mlxsw_hwmon, 323 MLXSW_HWMON_ATTR_TYPE_PWM, 324 type_index, num++); 325 } 326 return 0; 327 } 328 329 int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, 330 const struct mlxsw_bus_info *mlxsw_bus_info, 331 struct mlxsw_hwmon **p_hwmon) 332 { 333 struct mlxsw_hwmon *mlxsw_hwmon; 334 struct device *hwmon_dev; 335 int err; 336 337 mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon), 338 GFP_KERNEL); 339 if (!mlxsw_hwmon) 340 return -ENOMEM; 341 mlxsw_hwmon->core = mlxsw_core; 342 mlxsw_hwmon->bus_info = mlxsw_bus_info; 343 344 err = mlxsw_hwmon_temp_init(mlxsw_hwmon); 345 if (err) 346 goto err_temp_init; 347 348 err = mlxsw_hwmon_fans_init(mlxsw_hwmon); 349 if (err) 350 goto err_fans_init; 351 352 mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group; 353 mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs; 354 355 hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev, 356 "mlxsw", 357 mlxsw_hwmon, 358 mlxsw_hwmon->groups); 359 if (IS_ERR(hwmon_dev)) { 360 err = PTR_ERR(hwmon_dev); 361 goto err_hwmon_register; 362 } 363 364 mlxsw_hwmon->hwmon_dev = hwmon_dev; 365 *p_hwmon = mlxsw_hwmon; 366 return 0; 367 368 err_hwmon_register: 369 err_fans_init: 370 err_temp_init: 371 return err; 372 } 373