]> Git Repo - linux.git/blobdiff - drivers/hwmon/tmp421.c
fs/binfmt_elf: use PT_LOAD p_align values for static PIE
[linux.git] / drivers / hwmon / tmp421.c
index b963a369c5ab3069aa55568f706d4dc1af4aeeeb..1fd8d41d90c84a76c7d6467d3f75c52f85c6941a 100644 (file)
@@ -29,15 +29,20 @@ static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
 
 enum chips { tmp421, tmp422, tmp423, tmp441, tmp442 };
 
+#define MAX_CHANNELS                           4
 /* The TMP421 registers */
 #define TMP421_STATUS_REG                      0x08
 #define TMP421_CONFIG_REG_1                    0x09
+#define TMP421_CONFIG_REG_2                    0x0A
+#define TMP421_CONFIG_REG_REN(x)               (BIT(3 + (x)))
+#define TMP421_CONFIG_REG_REN_MASK             GENMASK(6, 3)
 #define TMP421_CONVERSION_RATE_REG             0x0B
+#define TMP421_N_FACTOR_REG_1                  0x21
 #define TMP421_MANUFACTURER_ID_REG             0xFE
 #define TMP421_DEVICE_ID_REG                   0xFF
 
-static const u8 TMP421_TEMP_MSB[4]             = { 0x00, 0x01, 0x02, 0x03 };
-static const u8 TMP421_TEMP_LSB[4]             = { 0x10, 0x11, 0x12, 0x13 };
+static const u8 TMP421_TEMP_MSB[MAX_CHANNELS]  = { 0x00, 0x01, 0x02, 0x03 };
+static const u8 TMP421_TEMP_LSB[MAX_CHANNELS]  = { 0x10, 0x11, 0x12, 0x13 };
 
 /* Flags */
 #define TMP421_CONFIG_SHUTDOWN                 0x40
@@ -86,18 +91,24 @@ static const struct of_device_id __maybe_unused tmp421_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, tmp421_of_match);
 
+struct tmp421_channel {
+       const char *label;
+       bool enabled;
+       s16 temp;
+};
+
 struct tmp421_data {
        struct i2c_client *client;
        struct mutex update_lock;
-       u32 temp_config[5];
+       u32 temp_config[MAX_CHANNELS + 1];
        struct hwmon_channel_info temp_info;
        const struct hwmon_channel_info *info[2];
        struct hwmon_chip_info chip;
-       char valid;
+       bool valid;
        unsigned long last_updated;
        unsigned long channels;
        u8 config;
-       s16 temp[4];
+       struct tmp421_channel channel[MAX_CHANNELS];
 };
 
 static int temp_from_raw(u16 reg, bool extended)
@@ -132,28 +143,56 @@ static int tmp421_update_device(struct tmp421_data *data)
                        ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]);
                        if (ret < 0)
                                goto exit;
-                       data->temp[i] = ret << 8;
+                       data->channel[i].temp = ret << 8;
 
                        ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_LSB[i]);
                        if (ret < 0)
                                goto exit;
-                       data->temp[i] |= ret;
+                       data->channel[i].temp |= ret;
                }
                data->last_updated = jiffies;
-               data->valid = 1;
+               data->valid = true;
        }
 
 exit:
        mutex_unlock(&data->update_lock);
 
        if (ret < 0) {
-               data->valid = 0;
+               data->valid = false;
                return ret;
        }
 
        return 0;
 }
 
+static int tmp421_enable_channels(struct tmp421_data *data)
+{
+       int err;
+       struct i2c_client *client = data->client;
+       struct device *dev = &client->dev;
+       int old = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_2);
+       int new, i;
+
+       if (old < 0) {
+               dev_err(dev, "error reading register, can't disable channels\n");
+               return old;
+       }
+
+       new = old & ~TMP421_CONFIG_REG_REN_MASK;
+       for (i = 0; i < data->channels; i++)
+               if (data->channel[i].enabled)
+                       new |= TMP421_CONFIG_REG_REN(i);
+
+       if (new == old)
+               return 0;
+
+       err = i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_2, new);
+       if (err < 0)
+               dev_err(dev, "error writing register, can't disable channels\n");
+
+       return err;
+}
+
 static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
                       u32 attr, int channel, long *val)
 {
@@ -166,15 +205,22 @@ static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
 
        switch (attr) {
        case hwmon_temp_input:
-               *val = temp_from_raw(tmp421->temp[channel],
+               if (!tmp421->channel[channel].enabled)
+                       return -ENODATA;
+               *val = temp_from_raw(tmp421->channel[channel].temp,
                                     tmp421->config & TMP421_CONFIG_RANGE);
                return 0;
        case hwmon_temp_fault:
+               if (!tmp421->channel[channel].enabled)
+                       return -ENODATA;
                /*
                 * Any of OPEN or /PVLD bits indicate a hardware mulfunction
                 * and the conversion result may be incorrect
                 */
-               *val = !!(tmp421->temp[channel] & 0x03);
+               *val = !!(tmp421->channel[channel].temp & 0x03);
+               return 0;
+       case hwmon_temp_enable:
+               *val = tmp421->channel[channel].enabled;
                return 0;
        default:
                return -EOPNOTSUPP;
@@ -182,6 +228,34 @@ static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
 
 }
 
+static int tmp421_read_string(struct device *dev, enum hwmon_sensor_types type,
+                            u32 attr, int channel, const char **str)
+{
+       struct tmp421_data *data = dev_get_drvdata(dev);
+
+       *str = data->channel[channel].label;
+
+       return 0;
+}
+
+static int tmp421_write(struct device *dev, enum hwmon_sensor_types type,
+                       u32 attr, int channel, long val)
+{
+       struct tmp421_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       switch (attr) {
+       case hwmon_temp_enable:
+               data->channel[channel].enabled = val;
+               ret = tmp421_enable_channels(data);
+               break;
+       default:
+           ret = -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
 static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
                                 u32 attr, int channel)
 {
@@ -189,14 +263,19 @@ static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
        case hwmon_temp_fault:
        case hwmon_temp_input:
                return 0444;
+       case hwmon_temp_label:
+               return 0444;
+       case hwmon_temp_enable:
+               return 0644;
        default:
                return 0;
        }
 }
 
-static int tmp421_init_client(struct i2c_client *client)
+static int tmp421_init_client(struct tmp421_data *data)
 {
        int config, config_orig;
+       struct i2c_client *client = data->client;
 
        /* Set the conversion rate to 2 Hz */
        i2c_smbus_write_byte_data(client, TMP421_CONVERSION_RATE_REG, 0x05);
@@ -217,7 +296,7 @@ static int tmp421_init_client(struct i2c_client *client)
                i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_1, config);
        }
 
-       return 0;
+       return tmp421_enable_channels(data);
 }
 
 static int tmp421_detect(struct i2c_client *client,
@@ -281,9 +360,78 @@ static int tmp421_detect(struct i2c_client *client,
        return 0;
 }
 
+static int tmp421_probe_child_from_dt(struct i2c_client *client,
+                                     struct device_node *child,
+                                     struct tmp421_data *data)
+
+{
+       struct device *dev = &client->dev;
+       u32 i;
+       s32 val;
+       int err;
+
+       err = of_property_read_u32(child, "reg", &i);
+       if (err) {
+               dev_err(dev, "missing reg property of %pOFn\n", child);
+               return err;
+       }
+
+       if (i >= data->channels) {
+               dev_err(dev, "invalid reg %d of %pOFn\n", i, child);
+               return -EINVAL;
+       }
+
+       of_property_read_string(child, "label", &data->channel[i].label);
+       if (data->channel[i].label)
+               data->temp_config[i] |= HWMON_T_LABEL;
+
+       data->channel[i].enabled = of_device_is_available(child);
+
+       err = of_property_read_s32(child, "ti,n-factor", &val);
+       if (!err) {
+               if (i == 0) {
+                       dev_err(dev, "n-factor can't be set for internal channel\n");
+                       return -EINVAL;
+               }
+
+               if (val > 127 || val < -128) {
+                       dev_err(dev, "n-factor for channel %d invalid (%d)\n",
+                               i, val);
+                       return -EINVAL;
+               }
+               i2c_smbus_write_byte_data(client, TMP421_N_FACTOR_REG_1 + i - 1,
+                                         val);
+       }
+
+       return 0;
+}
+
+static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *data)
+{
+       struct device *dev = &client->dev;
+       const struct device_node *np = dev->of_node;
+       struct device_node *child;
+       int err;
+
+       for_each_child_of_node(np, child) {
+               if (strcmp(child->name, "channel"))
+                       continue;
+
+               err = tmp421_probe_child_from_dt(client, child, data);
+               if (err) {
+                       of_node_put(child);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 static const struct hwmon_ops tmp421_ops = {
        .is_visible = tmp421_is_visible,
        .read = tmp421_read,
+       .read_string = tmp421_read_string,
+       .write = tmp421_write,
 };
 
 static int tmp421_probe(struct i2c_client *client)
@@ -305,12 +453,18 @@ static int tmp421_probe(struct i2c_client *client)
                data->channels = i2c_match_id(tmp421_id, client)->driver_data;
        data->client = client;
 
-       err = tmp421_init_client(client);
+       for (i = 0; i < data->channels; i++) {
+               data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_ENABLE;
+               data->channel[i].enabled = true;
+       }
+
+       err = tmp421_probe_from_dt(client, data);
        if (err)
                return err;
 
-       for (i = 0; i < data->channels; i++)
-               data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT;
+       err = tmp421_init_client(data);
+       if (err)
+               return err;
 
        data->chip.ops = &tmp421_ops;
        data->chip.info = data->info;
This page took 0.044745 seconds and 4 git commands to generate.