.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Hardware monitoring driver for Texas Instruments TPS53679 |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2017 Mellanox Technologies. All rights reserved. |
---|
5 | 6 | * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License as published by |
---|
9 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
10 | | - * (at your option) any later version. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | 7 | */ |
---|
17 | 8 | |
---|
| 9 | +#include <linux/bits.h> |
---|
18 | 10 | #include <linux/err.h> |
---|
19 | 11 | #include <linux/i2c.h> |
---|
20 | 12 | #include <linux/init.h> |
---|
21 | 13 | #include <linux/kernel.h> |
---|
22 | 14 | #include <linux/module.h> |
---|
| 15 | +#include <linux/of_device.h> |
---|
23 | 16 | #include "pmbus.h" |
---|
| 17 | + |
---|
| 18 | +enum chips { |
---|
| 19 | + tps53647, tps53667, tps53679, tps53681, tps53688 |
---|
| 20 | +}; |
---|
| 21 | + |
---|
| 22 | +#define TPS53647_PAGE_NUM 1 |
---|
24 | 23 | |
---|
25 | 24 | #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ |
---|
26 | 25 | #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ |
---|
.. | .. |
---|
29 | 28 | #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ |
---|
30 | 29 | #define TPS53679_PAGE_NUM 2 |
---|
31 | 30 | |
---|
32 | | -static int tps53679_identify(struct i2c_client *client, |
---|
33 | | - struct pmbus_driver_info *info) |
---|
| 31 | +#define TPS53681_DEVICE_ID 0x81 |
---|
| 32 | + |
---|
| 33 | +#define TPS53681_PMBUS_REVISION 0x33 |
---|
| 34 | + |
---|
| 35 | +#define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ |
---|
| 36 | + |
---|
| 37 | +static const struct i2c_device_id tps53679_id[]; |
---|
| 38 | + |
---|
| 39 | +static int tps53679_identify_mode(struct i2c_client *client, |
---|
| 40 | + struct pmbus_driver_info *info) |
---|
34 | 41 | { |
---|
35 | 42 | u8 vout_params; |
---|
36 | | - int ret; |
---|
| 43 | + int i, ret; |
---|
37 | 44 | |
---|
38 | | - /* Read the register with VOUT scaling value.*/ |
---|
39 | | - ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); |
---|
40 | | - if (ret < 0) |
---|
41 | | - return ret; |
---|
| 45 | + for (i = 0; i < info->pages; i++) { |
---|
| 46 | + /* Read the register with VOUT scaling value.*/ |
---|
| 47 | + ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); |
---|
| 48 | + if (ret < 0) |
---|
| 49 | + return ret; |
---|
42 | 50 | |
---|
43 | | - vout_params = ret & GENMASK(4, 0); |
---|
| 51 | + vout_params = ret & GENMASK(4, 0); |
---|
44 | 52 | |
---|
45 | | - switch (vout_params) { |
---|
46 | | - case TPS53679_PROT_VR13_10MV: |
---|
47 | | - case TPS53679_PROT_VR12_5_10MV: |
---|
48 | | - info->vrm_version = vr13; |
---|
49 | | - break; |
---|
50 | | - case TPS53679_PROT_VR13_5MV: |
---|
51 | | - case TPS53679_PROT_VR12_5MV: |
---|
52 | | - case TPS53679_PROT_IMVP8_5MV: |
---|
53 | | - info->vrm_version = vr12; |
---|
54 | | - break; |
---|
55 | | - default: |
---|
56 | | - return -EINVAL; |
---|
| 53 | + switch (vout_params) { |
---|
| 54 | + case TPS53679_PROT_VR13_10MV: |
---|
| 55 | + case TPS53679_PROT_VR12_5_10MV: |
---|
| 56 | + info->vrm_version[i] = vr13; |
---|
| 57 | + break; |
---|
| 58 | + case TPS53679_PROT_VR13_5MV: |
---|
| 59 | + case TPS53679_PROT_VR12_5MV: |
---|
| 60 | + case TPS53679_PROT_IMVP8_5MV: |
---|
| 61 | + info->vrm_version[i] = vr12; |
---|
| 62 | + break; |
---|
| 63 | + default: |
---|
| 64 | + return -EINVAL; |
---|
| 65 | + } |
---|
57 | 66 | } |
---|
58 | 67 | |
---|
59 | 68 | return 0; |
---|
60 | 69 | } |
---|
61 | 70 | |
---|
| 71 | +static int tps53679_identify_phases(struct i2c_client *client, |
---|
| 72 | + struct pmbus_driver_info *info) |
---|
| 73 | +{ |
---|
| 74 | + int ret; |
---|
| 75 | + |
---|
| 76 | + /* On TPS53681, only channel A provides per-phase output current */ |
---|
| 77 | + ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20); |
---|
| 78 | + if (ret < 0) |
---|
| 79 | + return ret; |
---|
| 80 | + info->phases[0] = (ret & 0x07) + 1; |
---|
| 81 | + |
---|
| 82 | + return 0; |
---|
| 83 | +} |
---|
| 84 | + |
---|
| 85 | +static int tps53679_identify_chip(struct i2c_client *client, |
---|
| 86 | + u8 revision, u16 id) |
---|
| 87 | +{ |
---|
| 88 | + u8 buf[I2C_SMBUS_BLOCK_MAX]; |
---|
| 89 | + int ret; |
---|
| 90 | + |
---|
| 91 | + ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); |
---|
| 92 | + if (ret < 0) |
---|
| 93 | + return ret; |
---|
| 94 | + if (ret != revision) { |
---|
| 95 | + dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret); |
---|
| 96 | + return -ENODEV; |
---|
| 97 | + } |
---|
| 98 | + |
---|
| 99 | + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); |
---|
| 100 | + if (ret < 0) |
---|
| 101 | + return ret; |
---|
| 102 | + if (ret != 1 || buf[0] != id) { |
---|
| 103 | + dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); |
---|
| 104 | + return -ENODEV; |
---|
| 105 | + } |
---|
| 106 | + return 0; |
---|
| 107 | +} |
---|
| 108 | + |
---|
| 109 | +/* |
---|
| 110 | + * Common identification function for chips with multi-phase support. |
---|
| 111 | + * Since those chips have special configuration registers, we want to have |
---|
| 112 | + * some level of reassurance that we are really talking with the chip |
---|
| 113 | + * being probed. Check PMBus revision and chip ID. |
---|
| 114 | + */ |
---|
| 115 | +static int tps53679_identify_multiphase(struct i2c_client *client, |
---|
| 116 | + struct pmbus_driver_info *info, |
---|
| 117 | + int pmbus_rev, int device_id) |
---|
| 118 | +{ |
---|
| 119 | + int ret; |
---|
| 120 | + |
---|
| 121 | + ret = tps53679_identify_chip(client, pmbus_rev, device_id); |
---|
| 122 | + if (ret < 0) |
---|
| 123 | + return ret; |
---|
| 124 | + |
---|
| 125 | + ret = tps53679_identify_mode(client, info); |
---|
| 126 | + if (ret < 0) |
---|
| 127 | + return ret; |
---|
| 128 | + |
---|
| 129 | + return tps53679_identify_phases(client, info); |
---|
| 130 | +} |
---|
| 131 | + |
---|
| 132 | +static int tps53679_identify(struct i2c_client *client, |
---|
| 133 | + struct pmbus_driver_info *info) |
---|
| 134 | +{ |
---|
| 135 | + return tps53679_identify_mode(client, info); |
---|
| 136 | +} |
---|
| 137 | + |
---|
| 138 | +static int tps53681_identify(struct i2c_client *client, |
---|
| 139 | + struct pmbus_driver_info *info) |
---|
| 140 | +{ |
---|
| 141 | + return tps53679_identify_multiphase(client, info, |
---|
| 142 | + TPS53681_PMBUS_REVISION, |
---|
| 143 | + TPS53681_DEVICE_ID); |
---|
| 144 | +} |
---|
| 145 | + |
---|
| 146 | +static int tps53681_read_word_data(struct i2c_client *client, int page, |
---|
| 147 | + int phase, int reg) |
---|
| 148 | +{ |
---|
| 149 | + /* |
---|
| 150 | + * For reading the total output current (READ_IOUT) for all phases, |
---|
| 151 | + * the chip datasheet is a bit vague. It says "PHASE must be set to |
---|
| 152 | + * FFh to access all phases simultaneously. PHASE may also be set to |
---|
| 153 | + * 80h readack (!) the total phase current". |
---|
| 154 | + * Experiments show that the command does _not_ report the total |
---|
| 155 | + * current for all phases if the phase is set to 0xff. Instead, it |
---|
| 156 | + * appears to report the current of one of the phases. Override phase |
---|
| 157 | + * parameter with 0x80 when reading the total output current on page 0. |
---|
| 158 | + */ |
---|
| 159 | + if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) |
---|
| 160 | + return pmbus_read_word_data(client, page, 0x80, reg); |
---|
| 161 | + return -ENODATA; |
---|
| 162 | +} |
---|
| 163 | + |
---|
62 | 164 | static struct pmbus_driver_info tps53679_info = { |
---|
63 | | - .pages = TPS53679_PAGE_NUM, |
---|
64 | 165 | .format[PSC_VOLTAGE_IN] = linear, |
---|
65 | 166 | .format[PSC_VOLTAGE_OUT] = vid, |
---|
66 | 167 | .format[PSC_TEMPERATURE] = linear, |
---|
67 | 168 | .format[PSC_CURRENT_OUT] = linear, |
---|
68 | 169 | .format[PSC_POWER] = linear, |
---|
69 | | - .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
---|
| 170 | + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | |
---|
| 171 | + PMBUS_HAVE_STATUS_INPUT | |
---|
| 172 | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
---|
70 | 173 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
---|
71 | 174 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
---|
72 | 175 | PMBUS_HAVE_POUT, |
---|
73 | | - .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
---|
| 176 | + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
---|
74 | 177 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
---|
75 | 178 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
---|
76 | 179 | PMBUS_HAVE_POUT, |
---|
77 | | - .identify = tps53679_identify, |
---|
| 180 | + .pfunc[0] = PMBUS_HAVE_IOUT, |
---|
| 181 | + .pfunc[1] = PMBUS_HAVE_IOUT, |
---|
| 182 | + .pfunc[2] = PMBUS_HAVE_IOUT, |
---|
| 183 | + .pfunc[3] = PMBUS_HAVE_IOUT, |
---|
| 184 | + .pfunc[4] = PMBUS_HAVE_IOUT, |
---|
| 185 | + .pfunc[5] = PMBUS_HAVE_IOUT, |
---|
78 | 186 | }; |
---|
79 | 187 | |
---|
80 | | -static int tps53679_probe(struct i2c_client *client, |
---|
81 | | - const struct i2c_device_id *id) |
---|
| 188 | +static int tps53679_probe(struct i2c_client *client) |
---|
82 | 189 | { |
---|
| 190 | + struct device *dev = &client->dev; |
---|
83 | 191 | struct pmbus_driver_info *info; |
---|
| 192 | + enum chips chip_id; |
---|
84 | 193 | |
---|
85 | | - info = devm_kmemdup(&client->dev, &tps53679_info, sizeof(*info), |
---|
86 | | - GFP_KERNEL); |
---|
| 194 | + if (dev->of_node) |
---|
| 195 | + chip_id = (enum chips)of_device_get_match_data(dev); |
---|
| 196 | + else |
---|
| 197 | + chip_id = i2c_match_id(tps53679_id, client)->driver_data; |
---|
| 198 | + |
---|
| 199 | + info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL); |
---|
87 | 200 | if (!info) |
---|
88 | 201 | return -ENOMEM; |
---|
89 | 202 | |
---|
90 | | - return pmbus_do_probe(client, id, info); |
---|
| 203 | + switch (chip_id) { |
---|
| 204 | + case tps53647: |
---|
| 205 | + case tps53667: |
---|
| 206 | + info->pages = TPS53647_PAGE_NUM; |
---|
| 207 | + info->identify = tps53679_identify; |
---|
| 208 | + break; |
---|
| 209 | + case tps53679: |
---|
| 210 | + case tps53688: |
---|
| 211 | + info->pages = TPS53679_PAGE_NUM; |
---|
| 212 | + info->identify = tps53679_identify; |
---|
| 213 | + break; |
---|
| 214 | + case tps53681: |
---|
| 215 | + info->pages = TPS53679_PAGE_NUM; |
---|
| 216 | + info->phases[0] = 6; |
---|
| 217 | + info->identify = tps53681_identify; |
---|
| 218 | + info->read_word_data = tps53681_read_word_data; |
---|
| 219 | + break; |
---|
| 220 | + default: |
---|
| 221 | + return -ENODEV; |
---|
| 222 | + } |
---|
| 223 | + |
---|
| 224 | + return pmbus_do_probe(client, info); |
---|
91 | 225 | } |
---|
92 | 226 | |
---|
93 | 227 | static const struct i2c_device_id tps53679_id[] = { |
---|
94 | | - {"tps53679", 0}, |
---|
| 228 | + {"tps53647", tps53647}, |
---|
| 229 | + {"tps53667", tps53667}, |
---|
| 230 | + {"tps53679", tps53679}, |
---|
| 231 | + {"tps53681", tps53681}, |
---|
| 232 | + {"tps53688", tps53688}, |
---|
95 | 233 | {} |
---|
96 | 234 | }; |
---|
97 | 235 | |
---|
98 | 236 | MODULE_DEVICE_TABLE(i2c, tps53679_id); |
---|
99 | 237 | |
---|
100 | | -static const struct of_device_id tps53679_of_match[] = { |
---|
101 | | - {.compatible = "ti,tps53679"}, |
---|
| 238 | +static const struct of_device_id __maybe_unused tps53679_of_match[] = { |
---|
| 239 | + {.compatible = "ti,tps53647", .data = (void *)tps53647}, |
---|
| 240 | + {.compatible = "ti,tps53667", .data = (void *)tps53667}, |
---|
| 241 | + {.compatible = "ti,tps53679", .data = (void *)tps53679}, |
---|
| 242 | + {.compatible = "ti,tps53681", .data = (void *)tps53681}, |
---|
| 243 | + {.compatible = "ti,tps53688", .data = (void *)tps53688}, |
---|
102 | 244 | {} |
---|
103 | 245 | }; |
---|
104 | 246 | MODULE_DEVICE_TABLE(of, tps53679_of_match); |
---|
.. | .. |
---|
108 | 250 | .name = "tps53679", |
---|
109 | 251 | .of_match_table = of_match_ptr(tps53679_of_match), |
---|
110 | 252 | }, |
---|
111 | | - .probe = tps53679_probe, |
---|
| 253 | + .probe_new = tps53679_probe, |
---|
112 | 254 | .remove = pmbus_do_remove, |
---|
113 | 255 | .id_table = tps53679_id, |
---|
114 | 256 | }; |
---|