root/drivers/hwmon/pmbus/ibm-cffps.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ibm_cffps_read_input_history
  2. ibm_cffps_debugfs_op
  3. ibm_cffps_read_byte_data
  4. ibm_cffps_read_word_data
  5. ibm_cffps_led_brightness_set
  6. ibm_cffps_led_blink_set
  7. ibm_cffps_create_led_class
  8. ibm_cffps_probe

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright 2017 IBM Corp.
   4  */
   5 
   6 #include <linux/bitops.h>
   7 #include <linux/debugfs.h>
   8 #include <linux/device.h>
   9 #include <linux/fs.h>
  10 #include <linux/i2c.h>
  11 #include <linux/jiffies.h>
  12 #include <linux/leds.h>
  13 #include <linux/module.h>
  14 #include <linux/mutex.h>
  15 #include <linux/of_device.h>
  16 #include <linux/pmbus.h>
  17 
  18 #include "pmbus.h"
  19 
  20 #define CFFPS_FRU_CMD                           0x9A
  21 #define CFFPS_PN_CMD                            0x9B
  22 #define CFFPS_SN_CMD                            0x9E
  23 #define CFFPS_CCIN_CMD                          0xBD
  24 #define CFFPS_FW_CMD                            0xFA
  25 #define CFFPS1_FW_NUM_BYTES                     4
  26 #define CFFPS2_FW_NUM_WORDS                     3
  27 #define CFFPS_SYS_CONFIG_CMD                    0xDA
  28 
  29 #define CFFPS_INPUT_HISTORY_CMD                 0xD6
  30 #define CFFPS_INPUT_HISTORY_SIZE                100
  31 
  32 /* STATUS_MFR_SPECIFIC bits */
  33 #define CFFPS_MFR_FAN_FAULT                     BIT(0)
  34 #define CFFPS_MFR_THERMAL_FAULT                 BIT(1)
  35 #define CFFPS_MFR_OV_FAULT                      BIT(2)
  36 #define CFFPS_MFR_UV_FAULT                      BIT(3)
  37 #define CFFPS_MFR_PS_KILL                       BIT(4)
  38 #define CFFPS_MFR_OC_FAULT                      BIT(5)
  39 #define CFFPS_MFR_VAUX_FAULT                    BIT(6)
  40 #define CFFPS_MFR_CURRENT_SHARE_WARNING         BIT(7)
  41 
  42 /*
  43  * LED off state actually relinquishes LED control to PSU firmware, so it can
  44  * turn on the LED for faults.
  45  */
  46 #define CFFPS_LED_OFF                           0
  47 #define CFFPS_LED_BLINK                         BIT(0)
  48 #define CFFPS_LED_ON                            BIT(1)
  49 #define CFFPS_BLINK_RATE_MS                     250
  50 
  51 enum {
  52         CFFPS_DEBUGFS_INPUT_HISTORY = 0,
  53         CFFPS_DEBUGFS_FRU,
  54         CFFPS_DEBUGFS_PN,
  55         CFFPS_DEBUGFS_SN,
  56         CFFPS_DEBUGFS_CCIN,
  57         CFFPS_DEBUGFS_FW,
  58         CFFPS_DEBUGFS_NUM_ENTRIES
  59 };
  60 
  61 enum versions { cffps1, cffps2 };
  62 
  63 struct ibm_cffps_input_history {
  64         struct mutex update_lock;
  65         unsigned long last_update;
  66 
  67         u8 byte_count;
  68         u8 data[CFFPS_INPUT_HISTORY_SIZE];
  69 };
  70 
  71 struct ibm_cffps {
  72         enum versions version;
  73         struct i2c_client *client;
  74 
  75         struct ibm_cffps_input_history input_history;
  76 
  77         int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
  78 
  79         char led_name[32];
  80         u8 led_state;
  81         struct led_classdev led;
  82 };
  83 
  84 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
  85 
  86 static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu,
  87                                             char __user *buf, size_t count,
  88                                             loff_t *ppos)
  89 {
  90         int rc;
  91         u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
  92         u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
  93         struct i2c_msg msg[2] = {
  94                 {
  95                         .addr = psu->client->addr,
  96                         .flags = psu->client->flags,
  97                         .len = 1,
  98                         .buf = msgbuf0,
  99                 }, {
 100                         .addr = psu->client->addr,
 101                         .flags = psu->client->flags | I2C_M_RD,
 102                         .len = CFFPS_INPUT_HISTORY_SIZE + 1,
 103                         .buf = msgbuf1,
 104                 },
 105         };
 106 
 107         if (!*ppos) {
 108                 mutex_lock(&psu->input_history.update_lock);
 109                 if (time_after(jiffies, psu->input_history.last_update + HZ)) {
 110                         /*
 111                          * Use a raw i2c transfer, since we need more bytes
 112                          * than Linux I2C supports through smbus xfr (only 32).
 113                          */
 114                         rc = i2c_transfer(psu->client->adapter, msg, 2);
 115                         if (rc < 0) {
 116                                 mutex_unlock(&psu->input_history.update_lock);
 117                                 return rc;
 118                         }
 119 
 120                         psu->input_history.byte_count = msgbuf1[0];
 121                         memcpy(psu->input_history.data, &msgbuf1[1],
 122                                CFFPS_INPUT_HISTORY_SIZE);
 123                         psu->input_history.last_update = jiffies;
 124                 }
 125 
 126                 mutex_unlock(&psu->input_history.update_lock);
 127         }
 128 
 129         return simple_read_from_buffer(buf, count, ppos,
 130                                        psu->input_history.data,
 131                                        psu->input_history.byte_count);
 132 }
 133 
 134 static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
 135                                     size_t count, loff_t *ppos)
 136 {
 137         u8 cmd;
 138         int i, rc;
 139         int *idxp = file->private_data;
 140         int idx = *idxp;
 141         struct ibm_cffps *psu = to_psu(idxp, idx);
 142         char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
 143 
 144         pmbus_set_page(psu->client, 0);
 145 
 146         switch (idx) {
 147         case CFFPS_DEBUGFS_INPUT_HISTORY:
 148                 return ibm_cffps_read_input_history(psu, buf, count, ppos);
 149         case CFFPS_DEBUGFS_FRU:
 150                 cmd = CFFPS_FRU_CMD;
 151                 break;
 152         case CFFPS_DEBUGFS_PN:
 153                 cmd = CFFPS_PN_CMD;
 154                 break;
 155         case CFFPS_DEBUGFS_SN:
 156                 cmd = CFFPS_SN_CMD;
 157                 break;
 158         case CFFPS_DEBUGFS_CCIN:
 159                 rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
 160                 if (rc < 0)
 161                         return rc;
 162 
 163                 rc = snprintf(data, 5, "%04X", rc);
 164                 goto done;
 165         case CFFPS_DEBUGFS_FW:
 166                 switch (psu->version) {
 167                 case cffps1:
 168                         for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
 169                                 rc = i2c_smbus_read_byte_data(psu->client,
 170                                                               CFFPS_FW_CMD +
 171                                                                 i);
 172                                 if (rc < 0)
 173                                         return rc;
 174 
 175                                 snprintf(&data[i * 2], 3, "%02X", rc);
 176                         }
 177 
 178                         rc = i * 2;
 179                         break;
 180                 case cffps2:
 181                         for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
 182                                 rc = i2c_smbus_read_word_data(psu->client,
 183                                                               CFFPS_FW_CMD +
 184                                                                 i);
 185                                 if (rc < 0)
 186                                         return rc;
 187 
 188                                 snprintf(&data[i * 4], 5, "%04X", rc);
 189                         }
 190 
 191                         rc = i * 4;
 192                         break;
 193                 default:
 194                         return -EOPNOTSUPP;
 195                 }
 196                 goto done;
 197         default:
 198                 return -EINVAL;
 199         }
 200 
 201         rc = i2c_smbus_read_block_data(psu->client, cmd, data);
 202         if (rc < 0)
 203                 return rc;
 204 
 205 done:
 206         data[rc] = '\n';
 207         rc += 2;
 208 
 209         return simple_read_from_buffer(buf, count, ppos, data, rc);
 210 }
 211 
 212 static const struct file_operations ibm_cffps_fops = {
 213         .llseek = noop_llseek,
 214         .read = ibm_cffps_debugfs_op,
 215         .open = simple_open,
 216 };
 217 
 218 static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
 219                                     int reg)
 220 {
 221         int rc, mfr;
 222 
 223         switch (reg) {
 224         case PMBUS_STATUS_VOUT:
 225         case PMBUS_STATUS_IOUT:
 226         case PMBUS_STATUS_TEMPERATURE:
 227         case PMBUS_STATUS_FAN_12:
 228                 rc = pmbus_read_byte_data(client, page, reg);
 229                 if (rc < 0)
 230                         return rc;
 231 
 232                 mfr = pmbus_read_byte_data(client, page,
 233                                            PMBUS_STATUS_MFR_SPECIFIC);
 234                 if (mfr < 0)
 235                         /*
 236                          * Return the status register instead of an error,
 237                          * since we successfully read status.
 238                          */
 239                         return rc;
 240 
 241                 /* Add MFR_SPECIFIC bits to the standard pmbus status regs. */
 242                 if (reg == PMBUS_STATUS_FAN_12) {
 243                         if (mfr & CFFPS_MFR_FAN_FAULT)
 244                                 rc |= PB_FAN_FAN1_FAULT;
 245                 } else if (reg == PMBUS_STATUS_TEMPERATURE) {
 246                         if (mfr & CFFPS_MFR_THERMAL_FAULT)
 247                                 rc |= PB_TEMP_OT_FAULT;
 248                 } else if (reg == PMBUS_STATUS_VOUT) {
 249                         if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT))
 250                                 rc |= PB_VOLTAGE_OV_FAULT;
 251                         if (mfr & CFFPS_MFR_UV_FAULT)
 252                                 rc |= PB_VOLTAGE_UV_FAULT;
 253                 } else if (reg == PMBUS_STATUS_IOUT) {
 254                         if (mfr & CFFPS_MFR_OC_FAULT)
 255                                 rc |= PB_IOUT_OC_FAULT;
 256                         if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING)
 257                                 rc |= PB_CURRENT_SHARE_FAULT;
 258                 }
 259                 break;
 260         default:
 261                 rc = -ENODATA;
 262                 break;
 263         }
 264 
 265         return rc;
 266 }
 267 
 268 static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
 269                                     int reg)
 270 {
 271         int rc, mfr;
 272 
 273         switch (reg) {
 274         case PMBUS_STATUS_WORD:
 275                 rc = pmbus_read_word_data(client, page, reg);
 276                 if (rc < 0)
 277                         return rc;
 278 
 279                 mfr = pmbus_read_byte_data(client, page,
 280                                            PMBUS_STATUS_MFR_SPECIFIC);
 281                 if (mfr < 0)
 282                         /*
 283                          * Return the status register instead of an error,
 284                          * since we successfully read status.
 285                          */
 286                         return rc;
 287 
 288                 if (mfr & CFFPS_MFR_PS_KILL)
 289                         rc |= PB_STATUS_OFF;
 290                 break;
 291         default:
 292                 rc = -ENODATA;
 293                 break;
 294         }
 295 
 296         return rc;
 297 }
 298 
 299 static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
 300                                         enum led_brightness brightness)
 301 {
 302         int rc;
 303         u8 next_led_state;
 304         struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
 305 
 306         if (brightness == LED_OFF) {
 307                 next_led_state = CFFPS_LED_OFF;
 308         } else {
 309                 brightness = LED_FULL;
 310 
 311                 if (psu->led_state != CFFPS_LED_BLINK)
 312                         next_led_state = CFFPS_LED_ON;
 313                 else
 314                         next_led_state = CFFPS_LED_BLINK;
 315         }
 316 
 317         dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n",
 318                 brightness, next_led_state);
 319 
 320         pmbus_set_page(psu->client, 0);
 321 
 322         rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
 323                                        next_led_state);
 324         if (rc < 0)
 325                 return rc;
 326 
 327         psu->led_state = next_led_state;
 328         led_cdev->brightness = brightness;
 329 
 330         return 0;
 331 }
 332 
 333 static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
 334                                    unsigned long *delay_on,
 335                                    unsigned long *delay_off)
 336 {
 337         int rc;
 338         struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
 339 
 340         dev_dbg(&psu->client->dev, "LED blink set.\n");
 341 
 342         pmbus_set_page(psu->client, 0);
 343 
 344         rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
 345                                        CFFPS_LED_BLINK);
 346         if (rc < 0)
 347                 return rc;
 348 
 349         psu->led_state = CFFPS_LED_BLINK;
 350         led_cdev->brightness = LED_FULL;
 351         *delay_on = CFFPS_BLINK_RATE_MS;
 352         *delay_off = CFFPS_BLINK_RATE_MS;
 353 
 354         return 0;
 355 }
 356 
 357 static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
 358 {
 359         int rc;
 360         struct i2c_client *client = psu->client;
 361         struct device *dev = &client->dev;
 362 
 363         snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
 364                  client->addr);
 365         psu->led.name = psu->led_name;
 366         psu->led.max_brightness = LED_FULL;
 367         psu->led.brightness_set_blocking = ibm_cffps_led_brightness_set;
 368         psu->led.blink_set = ibm_cffps_led_blink_set;
 369 
 370         rc = devm_led_classdev_register(dev, &psu->led);
 371         if (rc)
 372                 dev_warn(dev, "failed to register led class: %d\n", rc);
 373 }
 374 
 375 static struct pmbus_driver_info ibm_cffps_info[] = {
 376         [cffps1] = {
 377                 .pages = 1,
 378                 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
 379                         PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
 380                         PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
 381                         PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
 382                         PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
 383                         PMBUS_HAVE_STATUS_FAN12,
 384                 .read_byte_data = ibm_cffps_read_byte_data,
 385                 .read_word_data = ibm_cffps_read_word_data,
 386         },
 387         [cffps2] = {
 388                 .pages = 2,
 389                 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
 390                         PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
 391                         PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
 392                         PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
 393                         PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
 394                         PMBUS_HAVE_STATUS_FAN12,
 395                 .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
 396                         PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
 397                         PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT,
 398                 .read_byte_data = ibm_cffps_read_byte_data,
 399                 .read_word_data = ibm_cffps_read_word_data,
 400         },
 401 };
 402 
 403 static struct pmbus_platform_data ibm_cffps_pdata = {
 404         .flags = PMBUS_SKIP_STATUS_CHECK,
 405 };
 406 
 407 static int ibm_cffps_probe(struct i2c_client *client,
 408                            const struct i2c_device_id *id)
 409 {
 410         int i, rc;
 411         enum versions vs;
 412         struct dentry *debugfs;
 413         struct dentry *ibm_cffps_dir;
 414         struct ibm_cffps *psu;
 415         const void *md = of_device_get_match_data(&client->dev);
 416 
 417         if (md)
 418                 vs = (enum versions)md;
 419         else if (id)
 420                 vs = (enum versions)id->driver_data;
 421         else
 422                 vs = cffps1;
 423 
 424         client->dev.platform_data = &ibm_cffps_pdata;
 425         rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]);
 426         if (rc)
 427                 return rc;
 428 
 429         /*
 430          * Don't fail the probe if there isn't enough memory for leds and
 431          * debugfs.
 432          */
 433         psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
 434         if (!psu)
 435                 return 0;
 436 
 437         psu->version = vs;
 438         psu->client = client;
 439         mutex_init(&psu->input_history.update_lock);
 440         psu->input_history.last_update = jiffies - HZ;
 441 
 442         ibm_cffps_create_led_class(psu);
 443 
 444         /* Don't fail the probe if we can't create debugfs */
 445         debugfs = pmbus_get_debugfs_dir(client);
 446         if (!debugfs)
 447                 return 0;
 448 
 449         ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
 450         if (!ibm_cffps_dir)
 451                 return 0;
 452 
 453         for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
 454                 psu->debugfs_entries[i] = i;
 455 
 456         debugfs_create_file("input_history", 0444, ibm_cffps_dir,
 457                             &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
 458                             &ibm_cffps_fops);
 459         debugfs_create_file("fru", 0444, ibm_cffps_dir,
 460                             &psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
 461                             &ibm_cffps_fops);
 462         debugfs_create_file("part_number", 0444, ibm_cffps_dir,
 463                             &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
 464                             &ibm_cffps_fops);
 465         debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
 466                             &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
 467                             &ibm_cffps_fops);
 468         debugfs_create_file("ccin", 0444, ibm_cffps_dir,
 469                             &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
 470                             &ibm_cffps_fops);
 471         debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
 472                             &psu->debugfs_entries[CFFPS_DEBUGFS_FW],
 473                             &ibm_cffps_fops);
 474 
 475         return 0;
 476 }
 477 
 478 static const struct i2c_device_id ibm_cffps_id[] = {
 479         { "ibm_cffps1", cffps1 },
 480         { "ibm_cffps2", cffps2 },
 481         {}
 482 };
 483 MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
 484 
 485 static const struct of_device_id ibm_cffps_of_match[] = {
 486         {
 487                 .compatible = "ibm,cffps1",
 488                 .data = (void *)cffps1
 489         },
 490         {
 491                 .compatible = "ibm,cffps2",
 492                 .data = (void *)cffps2
 493         },
 494         {}
 495 };
 496 MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
 497 
 498 static struct i2c_driver ibm_cffps_driver = {
 499         .driver = {
 500                 .name = "ibm-cffps",
 501                 .of_match_table = ibm_cffps_of_match,
 502         },
 503         .probe = ibm_cffps_probe,
 504         .remove = pmbus_do_remove,
 505         .id_table = ibm_cffps_id,
 506 };
 507 
 508 module_i2c_driver(ibm_cffps_driver);
 509 
 510 MODULE_AUTHOR("Eddie James");
 511 MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
 512 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */