mlxsw: core: Extend thermal core with per inter-connect device thermal zones
authorVadim Pasternak <vadimp@mellanox.com>
Mon, 24 Jun 2019 10:32:01 +0000 (13:32 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 24 Jun 2019 15:15:41 +0000 (08:15 -0700)
Add a dedicated thermal zone for each inter-connect device. The
current temperature is obtained from inter-connect temperature sensor
and the default trip points are set to the same values as default ASIC
trip points. These settings could be changed from the user space.
A cooling device (fan) is bound to all inter-connect devices.

Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c

index cfab0e330a4713a50adc7f4cb4f755404c24a37b..88f43ad2cc4f22013e3be776f3c95fd507b694bd 100644 (file)
@@ -98,7 +98,7 @@ struct mlxsw_thermal_module {
        struct thermal_zone_device *tzdev;
        struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
        enum thermal_device_mode mode;
-       int module;
+       int module; /* Module or gearbox number */
 };
 
 struct mlxsw_thermal {
@@ -111,6 +111,8 @@ struct mlxsw_thermal {
        struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
        enum thermal_device_mode mode;
        struct mlxsw_thermal_module *tz_module_arr;
+       struct mlxsw_thermal_module *tz_gearbox_arr;
+       u8 tz_gearbox_num;
 };
 
 static inline u8 mlxsw_state_to_duty(int state)
@@ -554,6 +556,46 @@ static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
        .set_trip_hyst  = mlxsw_thermal_module_trip_hyst_set,
 };
 
+static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev,
+                                         int *p_temp)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+       struct mlxsw_thermal *thermal = tz->parent;
+       char mtmp_pl[MLXSW_REG_MTMP_LEN];
+       unsigned int temp;
+       u16 index;
+       int err;
+
+       index = MLXSW_REG_MTMP_GBOX_INDEX_MIN + tz->module;
+       mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false);
+
+       err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtmp), mtmp_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
+
+       *p_temp = (int) temp;
+       return 0;
+}
+
+static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = {
+       .bind           = mlxsw_thermal_module_bind,
+       .unbind         = mlxsw_thermal_module_unbind,
+       .get_mode       = mlxsw_thermal_module_mode_get,
+       .set_mode       = mlxsw_thermal_module_mode_set,
+       .get_temp       = mlxsw_thermal_gearbox_temp_get,
+       .get_trip_type  = mlxsw_thermal_module_trip_type_get,
+       .get_trip_temp  = mlxsw_thermal_module_trip_temp_get,
+       .set_trip_temp  = mlxsw_thermal_module_trip_temp_set,
+       .get_trip_hyst  = mlxsw_thermal_module_trip_hyst_get,
+       .set_trip_hyst  = mlxsw_thermal_module_trip_hyst_set,
+};
+
+static struct thermal_zone_params mlxsw_thermal_gearbox_params = {
+       .governor_name = "user_space",
+};
+
 static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev,
                                       unsigned long *p_state)
 {
@@ -779,6 +821,92 @@ mlxsw_thermal_modules_fini(struct mlxsw_thermal *thermal)
        kfree(thermal->tz_module_arr);
 }
 
+static int
+mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
+{
+       char tz_name[MLXSW_THERMAL_ZONE_MAX_NAME];
+
+       snprintf(tz_name, sizeof(tz_name), "mlxsw-gearbox%d",
+                gearbox_tz->module + 1);
+       gearbox_tz->tzdev = thermal_zone_device_register(tz_name,
+                                               MLXSW_THERMAL_NUM_TRIPS,
+                                               MLXSW_THERMAL_TRIP_MASK,
+                                               gearbox_tz,
+                                               &mlxsw_thermal_gearbox_ops,
+                                               &mlxsw_thermal_gearbox_params,
+                                               0, 0);
+       if (IS_ERR(gearbox_tz->tzdev))
+               return PTR_ERR(gearbox_tz->tzdev);
+
+       return 0;
+}
+
+static void
+mlxsw_thermal_gearbox_tz_fini(struct mlxsw_thermal_module *gearbox_tz)
+{
+       thermal_zone_device_unregister(gearbox_tz->tzdev);
+}
+
+static int
+mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
+                            struct mlxsw_thermal *thermal)
+{
+       struct mlxsw_thermal_module *gearbox_tz;
+       char mgpir_pl[MLXSW_REG_MGPIR_LEN];
+       int i;
+       int err;
+
+       if (!mlxsw_core_res_query_enabled(core))
+               return 0;
+
+       mlxsw_reg_mgpir_pack(mgpir_pl);
+       err = mlxsw_reg_query(core, MLXSW_REG(mgpir), mgpir_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mgpir_unpack(mgpir_pl, &thermal->tz_gearbox_num, NULL, NULL);
+       if (!thermal->tz_gearbox_num)
+               return 0;
+
+       thermal->tz_gearbox_arr = kcalloc(thermal->tz_gearbox_num,
+                                         sizeof(*thermal->tz_gearbox_arr),
+                                         GFP_KERNEL);
+       if (!thermal->tz_gearbox_arr)
+               return -ENOMEM;
+
+       for (i = 0; i < thermal->tz_gearbox_num; i++) {
+               gearbox_tz = &thermal->tz_gearbox_arr[i];
+               memcpy(gearbox_tz->trips, default_thermal_trips,
+                      sizeof(thermal->trips));
+               gearbox_tz->module = i;
+               gearbox_tz->parent = thermal;
+               err = mlxsw_thermal_gearbox_tz_init(gearbox_tz);
+               if (err)
+                       goto err_unreg_tz_gearbox;
+       }
+
+       return 0;
+
+err_unreg_tz_gearbox:
+       for (i--; i >= 0; i--)
+               mlxsw_thermal_gearbox_tz_fini(&thermal->tz_gearbox_arr[i]);
+       kfree(thermal->tz_gearbox_arr);
+       return err;
+}
+
+static void
+mlxsw_thermal_gearboxes_fini(struct mlxsw_thermal *thermal)
+{
+       int i;
+
+       if (!mlxsw_core_res_query_enabled(thermal->core))
+               return;
+
+       for (i = thermal->tz_gearbox_num - 1; i >= 0; i--)
+               mlxsw_thermal_gearbox_tz_fini(&thermal->tz_gearbox_arr[i]);
+       kfree(thermal->tz_gearbox_arr);
+}
+
 int mlxsw_thermal_init(struct mlxsw_core *core,
                       const struct mlxsw_bus_info *bus_info,
                       struct mlxsw_thermal **p_thermal)
@@ -869,10 +997,16 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
        if (err)
                goto err_unreg_tzdev;
 
+       err = mlxsw_thermal_gearboxes_init(dev, core, thermal);
+       if (err)
+               goto err_unreg_modules_tzdev;
+
        thermal->mode = THERMAL_DEVICE_ENABLED;
        *p_thermal = thermal;
        return 0;
 
+err_unreg_modules_tzdev:
+       mlxsw_thermal_modules_fini(thermal);
 err_unreg_tzdev:
        if (thermal->tzdev) {
                thermal_zone_device_unregister(thermal->tzdev);
@@ -891,6 +1025,7 @@ void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
 {
        int i;
 
+       mlxsw_thermal_gearboxes_fini(thermal);
        mlxsw_thermal_modules_fini(thermal);
        if (thermal->tzdev) {
                thermal_zone_device_unregister(thermal->tzdev);