19d2ecfb7SGuenter Roeck /* 29d2ecfb7SGuenter Roeck * Hardware monitoring driver for ucd9200 series Digital PWM System Controllers 39d2ecfb7SGuenter Roeck * 49d2ecfb7SGuenter Roeck * Copyright (C) 2011 Ericsson AB. 59d2ecfb7SGuenter Roeck * 69d2ecfb7SGuenter Roeck * This program is free software; you can redistribute it and/or modify 79d2ecfb7SGuenter Roeck * it under the terms of the GNU General Public License as published by 89d2ecfb7SGuenter Roeck * the Free Software Foundation; either version 2 of the License, or 99d2ecfb7SGuenter Roeck * (at your option) any later version. 109d2ecfb7SGuenter Roeck * 119d2ecfb7SGuenter Roeck * This program is distributed in the hope that it will be useful, 129d2ecfb7SGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of 139d2ecfb7SGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 149d2ecfb7SGuenter Roeck * GNU General Public License for more details. 159d2ecfb7SGuenter Roeck * 169d2ecfb7SGuenter Roeck * You should have received a copy of the GNU General Public License 179d2ecfb7SGuenter Roeck * along with this program; if not, write to the Free Software 189d2ecfb7SGuenter Roeck * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 199d2ecfb7SGuenter Roeck */ 209d2ecfb7SGuenter Roeck 219d2ecfb7SGuenter Roeck #include <linux/kernel.h> 229d2ecfb7SGuenter Roeck #include <linux/module.h> 2302331c3aSJavier Martinez Canillas #include <linux/of_device.h> 249d2ecfb7SGuenter Roeck #include <linux/init.h> 259d2ecfb7SGuenter Roeck #include <linux/err.h> 269d2ecfb7SGuenter Roeck #include <linux/slab.h> 279d2ecfb7SGuenter Roeck #include <linux/i2c.h> 284ba1bb12SWolfram Sang #include <linux/pmbus.h> 299d2ecfb7SGuenter Roeck #include "pmbus.h" 309d2ecfb7SGuenter Roeck 319d2ecfb7SGuenter Roeck #define UCD9200_PHASE_INFO 0xd2 329d2ecfb7SGuenter Roeck #define UCD9200_DEVICE_ID 0xfd 339d2ecfb7SGuenter Roeck 349d2ecfb7SGuenter Roeck enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246, 359d2ecfb7SGuenter Roeck ucd9248 }; 369d2ecfb7SGuenter Roeck 379d2ecfb7SGuenter Roeck static const struct i2c_device_id ucd9200_id[] = { 389d2ecfb7SGuenter Roeck {"ucd9200", ucd9200}, 399d2ecfb7SGuenter Roeck {"ucd9220", ucd9220}, 409d2ecfb7SGuenter Roeck {"ucd9222", ucd9222}, 419d2ecfb7SGuenter Roeck {"ucd9224", ucd9224}, 429d2ecfb7SGuenter Roeck {"ucd9240", ucd9240}, 439d2ecfb7SGuenter Roeck {"ucd9244", ucd9244}, 449d2ecfb7SGuenter Roeck {"ucd9246", ucd9246}, 459d2ecfb7SGuenter Roeck {"ucd9248", ucd9248}, 469d2ecfb7SGuenter Roeck {} 479d2ecfb7SGuenter Roeck }; 489d2ecfb7SGuenter Roeck MODULE_DEVICE_TABLE(i2c, ucd9200_id); 499d2ecfb7SGuenter Roeck 50*9c6b0f75SGuenter Roeck static const struct of_device_id __maybe_unused ucd9200_of_match[] = { 5102331c3aSJavier Martinez Canillas { 5202331c3aSJavier Martinez Canillas .compatible = "ti,cd9200", 5302331c3aSJavier Martinez Canillas .data = (void *)ucd9200 5402331c3aSJavier Martinez Canillas }, 5502331c3aSJavier Martinez Canillas { 5602331c3aSJavier Martinez Canillas .compatible = "ti,cd9220", 5702331c3aSJavier Martinez Canillas .data = (void *)ucd9220 5802331c3aSJavier Martinez Canillas }, 5902331c3aSJavier Martinez Canillas { 6002331c3aSJavier Martinez Canillas .compatible = "ti,cd9222", 6102331c3aSJavier Martinez Canillas .data = (void *)ucd9222 6202331c3aSJavier Martinez Canillas }, 6302331c3aSJavier Martinez Canillas { 6402331c3aSJavier Martinez Canillas .compatible = "ti,cd9224", 6502331c3aSJavier Martinez Canillas .data = (void *)ucd9224 6602331c3aSJavier Martinez Canillas }, 6702331c3aSJavier Martinez Canillas { 6802331c3aSJavier Martinez Canillas .compatible = "ti,cd9240", 6902331c3aSJavier Martinez Canillas .data = (void *)ucd9240 7002331c3aSJavier Martinez Canillas }, 7102331c3aSJavier Martinez Canillas { 7202331c3aSJavier Martinez Canillas .compatible = "ti,cd9244", 7302331c3aSJavier Martinez Canillas .data = (void *)ucd9244 7402331c3aSJavier Martinez Canillas }, 7502331c3aSJavier Martinez Canillas { 7602331c3aSJavier Martinez Canillas .compatible = "ti,cd9246", 7702331c3aSJavier Martinez Canillas .data = (void *)ucd9246 7802331c3aSJavier Martinez Canillas }, 7902331c3aSJavier Martinez Canillas { 8002331c3aSJavier Martinez Canillas .compatible = "ti,cd9248", 8102331c3aSJavier Martinez Canillas .data = (void *)ucd9248 8202331c3aSJavier Martinez Canillas }, 8302331c3aSJavier Martinez Canillas { }, 8402331c3aSJavier Martinez Canillas }; 8502331c3aSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, ucd9200_of_match); 8602331c3aSJavier Martinez Canillas 879d2ecfb7SGuenter Roeck static int ucd9200_probe(struct i2c_client *client, 889d2ecfb7SGuenter Roeck const struct i2c_device_id *id) 899d2ecfb7SGuenter Roeck { 909d2ecfb7SGuenter Roeck u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; 919d2ecfb7SGuenter Roeck struct pmbus_driver_info *info; 929d2ecfb7SGuenter Roeck const struct i2c_device_id *mid; 9302331c3aSJavier Martinez Canillas enum chips chip; 949d2ecfb7SGuenter Roeck int i, j, ret; 959d2ecfb7SGuenter Roeck 969d2ecfb7SGuenter Roeck if (!i2c_check_functionality(client->adapter, 979d2ecfb7SGuenter Roeck I2C_FUNC_SMBUS_BYTE_DATA | 989d2ecfb7SGuenter Roeck I2C_FUNC_SMBUS_BLOCK_DATA)) 999d2ecfb7SGuenter Roeck return -ENODEV; 1009d2ecfb7SGuenter Roeck 1019d2ecfb7SGuenter Roeck ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID, 1029d2ecfb7SGuenter Roeck block_buffer); 1039d2ecfb7SGuenter Roeck if (ret < 0) { 1049d2ecfb7SGuenter Roeck dev_err(&client->dev, "Failed to read device ID\n"); 1059d2ecfb7SGuenter Roeck return ret; 1069d2ecfb7SGuenter Roeck } 1079d2ecfb7SGuenter Roeck block_buffer[ret] = '\0'; 1089d2ecfb7SGuenter Roeck dev_info(&client->dev, "Device ID %s\n", block_buffer); 1099d2ecfb7SGuenter Roeck 110f020b007SJean Delvare for (mid = ucd9200_id; mid->name[0]; mid++) { 1119d2ecfb7SGuenter Roeck if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) 1129d2ecfb7SGuenter Roeck break; 1139d2ecfb7SGuenter Roeck } 114f020b007SJean Delvare if (!mid->name[0]) { 1159d2ecfb7SGuenter Roeck dev_err(&client->dev, "Unsupported device\n"); 1169d2ecfb7SGuenter Roeck return -ENODEV; 1179d2ecfb7SGuenter Roeck } 11802331c3aSJavier Martinez Canillas 11902331c3aSJavier Martinez Canillas if (client->dev.of_node) 12002331c3aSJavier Martinez Canillas chip = (enum chips)of_device_get_match_data(&client->dev); 12102331c3aSJavier Martinez Canillas else 12202331c3aSJavier Martinez Canillas chip = id->driver_data; 12302331c3aSJavier Martinez Canillas 12402331c3aSJavier Martinez Canillas if (chip != ucd9200 && chip != mid->driver_data) 1259d2ecfb7SGuenter Roeck dev_notice(&client->dev, 1269d2ecfb7SGuenter Roeck "Device mismatch: Configured %s, detected %s\n", 1279d2ecfb7SGuenter Roeck id->name, mid->name); 1289d2ecfb7SGuenter Roeck 1298b313ca7SGuenter Roeck info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), 1308b313ca7SGuenter Roeck GFP_KERNEL); 1319d2ecfb7SGuenter Roeck if (!info) 1329d2ecfb7SGuenter Roeck return -ENOMEM; 1339d2ecfb7SGuenter Roeck 1349d2ecfb7SGuenter Roeck ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO, 1359d2ecfb7SGuenter Roeck block_buffer); 1369d2ecfb7SGuenter Roeck if (ret < 0) { 1379d2ecfb7SGuenter Roeck dev_err(&client->dev, "Failed to read phase information\n"); 1388b313ca7SGuenter Roeck return ret; 1399d2ecfb7SGuenter Roeck } 1409d2ecfb7SGuenter Roeck 1419d2ecfb7SGuenter Roeck /* 1429d2ecfb7SGuenter Roeck * Calculate number of configured pages (rails) from PHASE_INFO 1439d2ecfb7SGuenter Roeck * register. 1449d2ecfb7SGuenter Roeck * Rails have to be sequential, so we can abort after finding 1459d2ecfb7SGuenter Roeck * the first unconfigured rail. 1469d2ecfb7SGuenter Roeck */ 1479d2ecfb7SGuenter Roeck info->pages = 0; 1489d2ecfb7SGuenter Roeck for (i = 0; i < ret; i++) { 1499d2ecfb7SGuenter Roeck if (!block_buffer[i]) 1509d2ecfb7SGuenter Roeck break; 1519d2ecfb7SGuenter Roeck info->pages++; 1529d2ecfb7SGuenter Roeck } 1539d2ecfb7SGuenter Roeck if (!info->pages) { 1549d2ecfb7SGuenter Roeck dev_err(&client->dev, "No rails configured\n"); 1558b313ca7SGuenter Roeck return -ENODEV; 1569d2ecfb7SGuenter Roeck } 1579d2ecfb7SGuenter Roeck dev_info(&client->dev, "%d rails configured\n", info->pages); 1589d2ecfb7SGuenter Roeck 1599d2ecfb7SGuenter Roeck /* 1609d2ecfb7SGuenter Roeck * Set PHASE registers on all pages to 0xff to ensure that phase 1619d2ecfb7SGuenter Roeck * specific commands will apply to all phases of a given page (rail). 1629d2ecfb7SGuenter Roeck * This only affects the READ_IOUT and READ_TEMPERATURE2 registers. 1639d2ecfb7SGuenter Roeck * READ_IOUT will return the sum of currents of all phases of a rail, 1649d2ecfb7SGuenter Roeck * and READ_TEMPERATURE2 will return the maximum temperature detected 1659d2ecfb7SGuenter Roeck * for the the phases of the rail. 1669d2ecfb7SGuenter Roeck */ 1679d2ecfb7SGuenter Roeck for (i = 0; i < info->pages; i++) { 1689d2ecfb7SGuenter Roeck /* 1699d2ecfb7SGuenter Roeck * Setting PAGE & PHASE fails once in a while for no obvious 1709d2ecfb7SGuenter Roeck * reason, so we need to retry a couple of times. 1719d2ecfb7SGuenter Roeck */ 1729d2ecfb7SGuenter Roeck for (j = 0; j < 3; j++) { 1739d2ecfb7SGuenter Roeck ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); 1749d2ecfb7SGuenter Roeck if (ret < 0) 1759d2ecfb7SGuenter Roeck continue; 1769d2ecfb7SGuenter Roeck ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE, 1779d2ecfb7SGuenter Roeck 0xff); 1789d2ecfb7SGuenter Roeck if (ret < 0) 1799d2ecfb7SGuenter Roeck continue; 1809d2ecfb7SGuenter Roeck break; 1819d2ecfb7SGuenter Roeck } 1829d2ecfb7SGuenter Roeck if (ret < 0) { 1839d2ecfb7SGuenter Roeck dev_err(&client->dev, 1849d2ecfb7SGuenter Roeck "Failed to initialize PHASE registers\n"); 1858b313ca7SGuenter Roeck return ret; 1869d2ecfb7SGuenter Roeck } 1879d2ecfb7SGuenter Roeck } 1889d2ecfb7SGuenter Roeck if (info->pages > 1) 1899d2ecfb7SGuenter Roeck i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); 1909d2ecfb7SGuenter Roeck 1919d2ecfb7SGuenter Roeck info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | 1929d2ecfb7SGuenter Roeck PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | 1939d2ecfb7SGuenter Roeck PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 1949d2ecfb7SGuenter Roeck PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 1959d2ecfb7SGuenter Roeck PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | 1969d2ecfb7SGuenter Roeck PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; 1979d2ecfb7SGuenter Roeck 1989d2ecfb7SGuenter Roeck for (i = 1; i < info->pages; i++) 1999d2ecfb7SGuenter Roeck info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 2009d2ecfb7SGuenter Roeck PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 2019d2ecfb7SGuenter Roeck PMBUS_HAVE_POUT | 2029d2ecfb7SGuenter Roeck PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; 2039d2ecfb7SGuenter Roeck 2049d2ecfb7SGuenter Roeck /* ucd9240 supports a single fan */ 2059d2ecfb7SGuenter Roeck if (mid->driver_data == ucd9240) 2069d2ecfb7SGuenter Roeck info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12; 2079d2ecfb7SGuenter Roeck 2088b313ca7SGuenter Roeck return pmbus_do_probe(client, mid, info); 2099d2ecfb7SGuenter Roeck } 2109d2ecfb7SGuenter Roeck 2119d2ecfb7SGuenter Roeck /* This is the driver that will be inserted */ 2129d2ecfb7SGuenter Roeck static struct i2c_driver ucd9200_driver = { 2139d2ecfb7SGuenter Roeck .driver = { 2149d2ecfb7SGuenter Roeck .name = "ucd9200", 21502331c3aSJavier Martinez Canillas .of_match_table = of_match_ptr(ucd9200_of_match), 2169d2ecfb7SGuenter Roeck }, 2179d2ecfb7SGuenter Roeck .probe = ucd9200_probe, 218dd285ad7SGuenter Roeck .remove = pmbus_do_remove, 2199d2ecfb7SGuenter Roeck .id_table = ucd9200_id, 2209d2ecfb7SGuenter Roeck }; 2219d2ecfb7SGuenter Roeck 222f0967eeaSAxel Lin module_i2c_driver(ucd9200_driver); 2239d2ecfb7SGuenter Roeck 2249d2ecfb7SGuenter Roeck MODULE_AUTHOR("Guenter Roeck"); 2259d2ecfb7SGuenter Roeck MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); 2269d2ecfb7SGuenter Roeck MODULE_LICENSE("GPL"); 227