| .. | .. |
|---|
| 19 | 19 | #include <linux/err.h> |
|---|
| 20 | 20 | #include <linux/mutex.h> |
|---|
| 21 | 21 | #include <linux/of.h> |
|---|
| 22 | +#include <linux/regmap.h> |
|---|
| 22 | 23 | |
|---|
| 23 | 24 | /* Addresses to scan */ |
|---|
| 24 | 25 | static const unsigned short normal_i2c[] = { |
|---|
| .. | .. |
|---|
| 189 | 190 | { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK }, |
|---|
| 190 | 191 | }; |
|---|
| 191 | 192 | |
|---|
| 192 | | -enum temp_index { |
|---|
| 193 | | - t_input = 0, |
|---|
| 194 | | - t_crit, |
|---|
| 195 | | - t_min, |
|---|
| 196 | | - t_max, |
|---|
| 197 | | - t_num_temp |
|---|
| 198 | | -}; |
|---|
| 199 | | - |
|---|
| 200 | | -static const u8 temp_regs[t_num_temp] = { |
|---|
| 201 | | - [t_input] = JC42_REG_TEMP, |
|---|
| 202 | | - [t_crit] = JC42_REG_TEMP_CRITICAL, |
|---|
| 203 | | - [t_min] = JC42_REG_TEMP_LOWER, |
|---|
| 204 | | - [t_max] = JC42_REG_TEMP_UPPER, |
|---|
| 205 | | -}; |
|---|
| 206 | | - |
|---|
| 207 | 193 | /* Each client has this additional data */ |
|---|
| 208 | 194 | struct jc42_data { |
|---|
| 209 | | - struct i2c_client *client; |
|---|
| 210 | 195 | struct mutex update_lock; /* protect register access */ |
|---|
| 196 | + struct regmap *regmap; |
|---|
| 211 | 197 | bool extended; /* true if extended range supported */ |
|---|
| 212 | 198 | bool valid; |
|---|
| 213 | | - unsigned long last_updated; /* In jiffies */ |
|---|
| 214 | 199 | u16 orig_config; /* original configuration */ |
|---|
| 215 | 200 | u16 config; /* current configuration */ |
|---|
| 216 | | - u16 temp[t_num_temp];/* Temperatures */ |
|---|
| 217 | 201 | }; |
|---|
| 218 | 202 | |
|---|
| 219 | 203 | #define JC42_TEMP_MIN_EXTENDED (-40000) |
|---|
| .. | .. |
|---|
| 238 | 222 | return reg * 125 / 2; |
|---|
| 239 | 223 | } |
|---|
| 240 | 224 | |
|---|
| 241 | | -static struct jc42_data *jc42_update_device(struct device *dev) |
|---|
| 242 | | -{ |
|---|
| 243 | | - struct jc42_data *data = dev_get_drvdata(dev); |
|---|
| 244 | | - struct i2c_client *client = data->client; |
|---|
| 245 | | - struct jc42_data *ret = data; |
|---|
| 246 | | - int i, val; |
|---|
| 247 | | - |
|---|
| 248 | | - mutex_lock(&data->update_lock); |
|---|
| 249 | | - |
|---|
| 250 | | - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
|---|
| 251 | | - for (i = 0; i < t_num_temp; i++) { |
|---|
| 252 | | - val = i2c_smbus_read_word_swapped(client, temp_regs[i]); |
|---|
| 253 | | - if (val < 0) { |
|---|
| 254 | | - ret = ERR_PTR(val); |
|---|
| 255 | | - goto abort; |
|---|
| 256 | | - } |
|---|
| 257 | | - data->temp[i] = val; |
|---|
| 258 | | - } |
|---|
| 259 | | - data->last_updated = jiffies; |
|---|
| 260 | | - data->valid = true; |
|---|
| 261 | | - } |
|---|
| 262 | | -abort: |
|---|
| 263 | | - mutex_unlock(&data->update_lock); |
|---|
| 264 | | - return ret; |
|---|
| 265 | | -} |
|---|
| 266 | | - |
|---|
| 267 | 225 | static int jc42_read(struct device *dev, enum hwmon_sensor_types type, |
|---|
| 268 | 226 | u32 attr, int channel, long *val) |
|---|
| 269 | 227 | { |
|---|
| 270 | | - struct jc42_data *data = jc42_update_device(dev); |
|---|
| 271 | | - int temp, hyst; |
|---|
| 228 | + struct jc42_data *data = dev_get_drvdata(dev); |
|---|
| 229 | + unsigned int regval; |
|---|
| 230 | + int ret, temp, hyst; |
|---|
| 272 | 231 | |
|---|
| 273 | | - if (IS_ERR(data)) |
|---|
| 274 | | - return PTR_ERR(data); |
|---|
| 232 | + mutex_lock(&data->update_lock); |
|---|
| 275 | 233 | |
|---|
| 276 | 234 | switch (attr) { |
|---|
| 277 | 235 | case hwmon_temp_input: |
|---|
| 278 | | - *val = jc42_temp_from_reg(data->temp[t_input]); |
|---|
| 279 | | - return 0; |
|---|
| 236 | + ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val); |
|---|
| 237 | + if (ret) |
|---|
| 238 | + break; |
|---|
| 239 | + |
|---|
| 240 | + *val = jc42_temp_from_reg(regval); |
|---|
| 241 | + break; |
|---|
| 280 | 242 | case hwmon_temp_min: |
|---|
| 281 | | - *val = jc42_temp_from_reg(data->temp[t_min]); |
|---|
| 282 | | - return 0; |
|---|
| 243 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, ®val); |
|---|
| 244 | + if (ret) |
|---|
| 245 | + break; |
|---|
| 246 | + |
|---|
| 247 | + *val = jc42_temp_from_reg(regval); |
|---|
| 248 | + break; |
|---|
| 283 | 249 | case hwmon_temp_max: |
|---|
| 284 | | - *val = jc42_temp_from_reg(data->temp[t_max]); |
|---|
| 285 | | - return 0; |
|---|
| 250 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, ®val); |
|---|
| 251 | + if (ret) |
|---|
| 252 | + break; |
|---|
| 253 | + |
|---|
| 254 | + *val = jc42_temp_from_reg(regval); |
|---|
| 255 | + break; |
|---|
| 286 | 256 | case hwmon_temp_crit: |
|---|
| 287 | | - *val = jc42_temp_from_reg(data->temp[t_crit]); |
|---|
| 288 | | - return 0; |
|---|
| 257 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL, |
|---|
| 258 | + ®val); |
|---|
| 259 | + if (ret) |
|---|
| 260 | + break; |
|---|
| 261 | + |
|---|
| 262 | + *val = jc42_temp_from_reg(regval); |
|---|
| 263 | + break; |
|---|
| 289 | 264 | case hwmon_temp_max_hyst: |
|---|
| 290 | | - temp = jc42_temp_from_reg(data->temp[t_max]); |
|---|
| 265 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, ®val); |
|---|
| 266 | + if (ret) |
|---|
| 267 | + break; |
|---|
| 268 | + |
|---|
| 269 | + temp = jc42_temp_from_reg(regval); |
|---|
| 291 | 270 | hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) |
|---|
| 292 | 271 | >> JC42_CFG_HYST_SHIFT]; |
|---|
| 293 | 272 | *val = temp - hyst; |
|---|
| 294 | | - return 0; |
|---|
| 273 | + break; |
|---|
| 295 | 274 | case hwmon_temp_crit_hyst: |
|---|
| 296 | | - temp = jc42_temp_from_reg(data->temp[t_crit]); |
|---|
| 275 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL, |
|---|
| 276 | + ®val); |
|---|
| 277 | + if (ret) |
|---|
| 278 | + break; |
|---|
| 279 | + |
|---|
| 280 | + temp = jc42_temp_from_reg(regval); |
|---|
| 297 | 281 | hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) |
|---|
| 298 | 282 | >> JC42_CFG_HYST_SHIFT]; |
|---|
| 299 | 283 | *val = temp - hyst; |
|---|
| 300 | | - return 0; |
|---|
| 284 | + break; |
|---|
| 301 | 285 | case hwmon_temp_min_alarm: |
|---|
| 302 | | - *val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1; |
|---|
| 303 | | - return 0; |
|---|
| 286 | + ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val); |
|---|
| 287 | + if (ret) |
|---|
| 288 | + break; |
|---|
| 289 | + |
|---|
| 290 | + *val = (regval >> JC42_ALARM_MIN_BIT) & 1; |
|---|
| 291 | + break; |
|---|
| 304 | 292 | case hwmon_temp_max_alarm: |
|---|
| 305 | | - *val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1; |
|---|
| 306 | | - return 0; |
|---|
| 293 | + ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val); |
|---|
| 294 | + if (ret) |
|---|
| 295 | + break; |
|---|
| 296 | + |
|---|
| 297 | + *val = (regval >> JC42_ALARM_MAX_BIT) & 1; |
|---|
| 298 | + break; |
|---|
| 307 | 299 | case hwmon_temp_crit_alarm: |
|---|
| 308 | | - *val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1; |
|---|
| 309 | | - return 0; |
|---|
| 300 | + ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val); |
|---|
| 301 | + if (ret) |
|---|
| 302 | + break; |
|---|
| 303 | + |
|---|
| 304 | + *val = (regval >> JC42_ALARM_CRIT_BIT) & 1; |
|---|
| 305 | + break; |
|---|
| 310 | 306 | default: |
|---|
| 311 | | - return -EOPNOTSUPP; |
|---|
| 307 | + ret = -EOPNOTSUPP; |
|---|
| 308 | + break; |
|---|
| 312 | 309 | } |
|---|
| 310 | + |
|---|
| 311 | + mutex_unlock(&data->update_lock); |
|---|
| 312 | + |
|---|
| 313 | + return ret; |
|---|
| 313 | 314 | } |
|---|
| 314 | 315 | |
|---|
| 315 | 316 | static int jc42_write(struct device *dev, enum hwmon_sensor_types type, |
|---|
| 316 | 317 | u32 attr, int channel, long val) |
|---|
| 317 | 318 | { |
|---|
| 318 | 319 | struct jc42_data *data = dev_get_drvdata(dev); |
|---|
| 319 | | - struct i2c_client *client = data->client; |
|---|
| 320 | + unsigned int regval; |
|---|
| 320 | 321 | int diff, hyst; |
|---|
| 321 | 322 | int ret; |
|---|
| 322 | 323 | |
|---|
| .. | .. |
|---|
| 324 | 325 | |
|---|
| 325 | 326 | switch (attr) { |
|---|
| 326 | 327 | case hwmon_temp_min: |
|---|
| 327 | | - data->temp[t_min] = jc42_temp_to_reg(val, data->extended); |
|---|
| 328 | | - ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min], |
|---|
| 329 | | - data->temp[t_min]); |
|---|
| 328 | + ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER, |
|---|
| 329 | + jc42_temp_to_reg(val, data->extended)); |
|---|
| 330 | 330 | break; |
|---|
| 331 | 331 | case hwmon_temp_max: |
|---|
| 332 | | - data->temp[t_max] = jc42_temp_to_reg(val, data->extended); |
|---|
| 333 | | - ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max], |
|---|
| 334 | | - data->temp[t_max]); |
|---|
| 332 | + ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER, |
|---|
| 333 | + jc42_temp_to_reg(val, data->extended)); |
|---|
| 335 | 334 | break; |
|---|
| 336 | 335 | case hwmon_temp_crit: |
|---|
| 337 | | - data->temp[t_crit] = jc42_temp_to_reg(val, data->extended); |
|---|
| 338 | | - ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit], |
|---|
| 339 | | - data->temp[t_crit]); |
|---|
| 336 | + ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL, |
|---|
| 337 | + jc42_temp_to_reg(val, data->extended)); |
|---|
| 340 | 338 | break; |
|---|
| 341 | 339 | case hwmon_temp_crit_hyst: |
|---|
| 340 | + ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL, |
|---|
| 341 | + ®val); |
|---|
| 342 | + if (ret) |
|---|
| 343 | + break; |
|---|
| 344 | + |
|---|
| 342 | 345 | /* |
|---|
| 343 | 346 | * JC42.4 compliant chips only support four hysteresis values. |
|---|
| 344 | 347 | * Pick best choice and go from there. |
|---|
| .. | .. |
|---|
| 346 | 349 | val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED |
|---|
| 347 | 350 | : JC42_TEMP_MIN) - 6000, |
|---|
| 348 | 351 | JC42_TEMP_MAX); |
|---|
| 349 | | - diff = jc42_temp_from_reg(data->temp[t_crit]) - val; |
|---|
| 352 | + diff = jc42_temp_from_reg(regval) - val; |
|---|
| 350 | 353 | hyst = 0; |
|---|
| 351 | 354 | if (diff > 0) { |
|---|
| 352 | 355 | if (diff < 2250) |
|---|
| .. | .. |
|---|
| 358 | 361 | } |
|---|
| 359 | 362 | data->config = (data->config & ~JC42_CFG_HYST_MASK) | |
|---|
| 360 | 363 | (hyst << JC42_CFG_HYST_SHIFT); |
|---|
| 361 | | - ret = i2c_smbus_write_word_swapped(data->client, |
|---|
| 362 | | - JC42_REG_CONFIG, |
|---|
| 363 | | - data->config); |
|---|
| 364 | + ret = regmap_write(data->regmap, JC42_REG_CONFIG, |
|---|
| 365 | + data->config); |
|---|
| 364 | 366 | break; |
|---|
| 365 | 367 | default: |
|---|
| 366 | 368 | ret = -EOPNOTSUPP; |
|---|
| .. | .. |
|---|
| 458 | 460 | .info = jc42_info, |
|---|
| 459 | 461 | }; |
|---|
| 460 | 462 | |
|---|
| 463 | +static bool jc42_readable_reg(struct device *dev, unsigned int reg) |
|---|
| 464 | +{ |
|---|
| 465 | + return (reg >= JC42_REG_CAP && reg <= JC42_REG_DEVICEID) || |
|---|
| 466 | + reg == JC42_REG_SMBUS; |
|---|
| 467 | +} |
|---|
| 468 | + |
|---|
| 469 | +static bool jc42_writable_reg(struct device *dev, unsigned int reg) |
|---|
| 470 | +{ |
|---|
| 471 | + return (reg >= JC42_REG_CONFIG && reg <= JC42_REG_TEMP_CRITICAL) || |
|---|
| 472 | + reg == JC42_REG_SMBUS; |
|---|
| 473 | +} |
|---|
| 474 | + |
|---|
| 475 | +static bool jc42_volatile_reg(struct device *dev, unsigned int reg) |
|---|
| 476 | +{ |
|---|
| 477 | + return reg == JC42_REG_CONFIG || reg == JC42_REG_TEMP; |
|---|
| 478 | +} |
|---|
| 479 | + |
|---|
| 480 | +static const struct regmap_config jc42_regmap_config = { |
|---|
| 481 | + .reg_bits = 8, |
|---|
| 482 | + .val_bits = 16, |
|---|
| 483 | + .val_format_endian = REGMAP_ENDIAN_BIG, |
|---|
| 484 | + .max_register = JC42_REG_SMBUS, |
|---|
| 485 | + .writeable_reg = jc42_writable_reg, |
|---|
| 486 | + .readable_reg = jc42_readable_reg, |
|---|
| 487 | + .volatile_reg = jc42_volatile_reg, |
|---|
| 488 | + .cache_type = REGCACHE_RBTREE, |
|---|
| 489 | +}; |
|---|
| 490 | + |
|---|
| 461 | 491 | static int jc42_probe(struct i2c_client *client) |
|---|
| 462 | 492 | { |
|---|
| 463 | 493 | struct device *dev = &client->dev; |
|---|
| 464 | 494 | struct device *hwmon_dev; |
|---|
| 495 | + unsigned int config, cap; |
|---|
| 465 | 496 | struct jc42_data *data; |
|---|
| 466 | | - int config, cap; |
|---|
| 497 | + int ret; |
|---|
| 467 | 498 | |
|---|
| 468 | 499 | data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL); |
|---|
| 469 | 500 | if (!data) |
|---|
| 470 | 501 | return -ENOMEM; |
|---|
| 471 | 502 | |
|---|
| 472 | | - data->client = client; |
|---|
| 503 | + data->regmap = devm_regmap_init_i2c(client, &jc42_regmap_config); |
|---|
| 504 | + if (IS_ERR(data->regmap)) |
|---|
| 505 | + return PTR_ERR(data->regmap); |
|---|
| 506 | + |
|---|
| 473 | 507 | i2c_set_clientdata(client, data); |
|---|
| 474 | 508 | mutex_init(&data->update_lock); |
|---|
| 475 | 509 | |
|---|
| 476 | | - cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP); |
|---|
| 477 | | - if (cap < 0) |
|---|
| 478 | | - return cap; |
|---|
| 510 | + ret = regmap_read(data->regmap, JC42_REG_CAP, &cap); |
|---|
| 511 | + if (ret) |
|---|
| 512 | + return ret; |
|---|
| 479 | 513 | |
|---|
| 480 | 514 | data->extended = !!(cap & JC42_CAP_RANGE); |
|---|
| 481 | 515 | |
|---|
| 482 | 516 | if (device_property_read_bool(dev, "smbus-timeout-disable")) { |
|---|
| 483 | | - int smbus; |
|---|
| 484 | | - |
|---|
| 485 | 517 | /* |
|---|
| 486 | 518 | * Not all chips support this register, but from a |
|---|
| 487 | 519 | * quick read of various datasheets no chip appears |
|---|
| 488 | 520 | * incompatible with the below attempt to disable |
|---|
| 489 | 521 | * the timeout. And the whole thing is opt-in... |
|---|
| 490 | 522 | */ |
|---|
| 491 | | - smbus = i2c_smbus_read_word_swapped(client, JC42_REG_SMBUS); |
|---|
| 492 | | - if (smbus < 0) |
|---|
| 493 | | - return smbus; |
|---|
| 494 | | - i2c_smbus_write_word_swapped(client, JC42_REG_SMBUS, |
|---|
| 495 | | - smbus | SMBUS_STMOUT); |
|---|
| 523 | + ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS, |
|---|
| 524 | + SMBUS_STMOUT); |
|---|
| 525 | + if (ret) |
|---|
| 526 | + return ret; |
|---|
| 496 | 527 | } |
|---|
| 497 | 528 | |
|---|
| 498 | | - config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG); |
|---|
| 499 | | - if (config < 0) |
|---|
| 500 | | - return config; |
|---|
| 529 | + ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config); |
|---|
| 530 | + if (ret) |
|---|
| 531 | + return ret; |
|---|
| 501 | 532 | |
|---|
| 502 | 533 | data->orig_config = config; |
|---|
| 503 | 534 | if (config & JC42_CFG_SHUTDOWN) { |
|---|
| 504 | 535 | config &= ~JC42_CFG_SHUTDOWN; |
|---|
| 505 | | - i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); |
|---|
| 536 | + regmap_write(data->regmap, JC42_REG_CONFIG, config); |
|---|
| 506 | 537 | } |
|---|
| 507 | 538 | data->config = config; |
|---|
| 508 | 539 | |
|---|
| .. | .. |
|---|
| 523 | 554 | |
|---|
| 524 | 555 | config = (data->orig_config & ~JC42_CFG_HYST_MASK) |
|---|
| 525 | 556 | | (data->config & JC42_CFG_HYST_MASK); |
|---|
| 526 | | - i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); |
|---|
| 557 | + regmap_write(data->regmap, JC42_REG_CONFIG, config); |
|---|
| 527 | 558 | } |
|---|
| 528 | 559 | return 0; |
|---|
| 529 | 560 | } |
|---|
| .. | .. |
|---|
| 535 | 566 | struct jc42_data *data = dev_get_drvdata(dev); |
|---|
| 536 | 567 | |
|---|
| 537 | 568 | data->config |= JC42_CFG_SHUTDOWN; |
|---|
| 538 | | - i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, |
|---|
| 539 | | - data->config); |
|---|
| 569 | + regmap_write(data->regmap, JC42_REG_CONFIG, data->config); |
|---|
| 570 | + |
|---|
| 571 | + regcache_cache_only(data->regmap, true); |
|---|
| 572 | + regcache_mark_dirty(data->regmap); |
|---|
| 573 | + |
|---|
| 540 | 574 | return 0; |
|---|
| 541 | 575 | } |
|---|
| 542 | 576 | |
|---|
| .. | .. |
|---|
| 544 | 578 | { |
|---|
| 545 | 579 | struct jc42_data *data = dev_get_drvdata(dev); |
|---|
| 546 | 580 | |
|---|
| 581 | + regcache_cache_only(data->regmap, false); |
|---|
| 582 | + |
|---|
| 547 | 583 | data->config &= ~JC42_CFG_SHUTDOWN; |
|---|
| 548 | | - i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, |
|---|
| 549 | | - data->config); |
|---|
| 550 | | - return 0; |
|---|
| 584 | + regmap_write(data->regmap, JC42_REG_CONFIG, data->config); |
|---|
| 585 | + |
|---|
| 586 | + /* Restore cached register values to hardware */ |
|---|
| 587 | + return regcache_sync(data->regmap); |
|---|
| 551 | 588 | } |
|---|
| 552 | 589 | |
|---|
| 553 | 590 | static const struct dev_pm_ops jc42_dev_pm_ops = { |
|---|