| 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | |
| 3 | // documentation of this device is available at |
| 4 | // https://www.nxp.com/docs/en/data-sheet/PF5300.pdf |
| 5 | |
| 6 | #include <linux/delay.h> |
| 7 | #include <linux/err.h> |
| 8 | #include <linux/gpio/consumer.h> |
| 9 | #include <linux/i2c.h> |
| 10 | #include <linux/module.h> |
| 11 | #include <linux/regmap.h> |
| 12 | #include <linux/regulator/driver.h> |
| 13 | #include <linux/regulator/consumer.h> |
| 14 | #include <linux/regulator/machine.h> |
| 15 | #include <linux/regulator/of_regulator.h> |
| 16 | |
| 17 | /* registers */ |
| 18 | #define PF530X_DEVICEID 0x00 |
| 19 | #define PF530X_REV 0x01 |
| 20 | #define PF530X_EMREV 0x02 |
| 21 | #define PF530X_PROGID 0x03 |
| 22 | #define PF530X_CONFIG1 0x04 |
| 23 | #define PF530X_INT_STATUS1 0x05 |
| 24 | #define PF530X_INT_SENSE1 0x06 |
| 25 | #define PF530X_INT_STATUS2 0x07 |
| 26 | #define PF530X_INT_SENSE2 0x08 |
| 27 | #define PF530X_BIST_STAT1 0x09 |
| 28 | #define PF530X_BIST_CTRL 0x0a |
| 29 | #define PF530X_STATE 0x0b |
| 30 | #define PF530X_STATE_CTRL 0x0c |
| 31 | #define PF530X_SW1_VOLT 0x0d |
| 32 | #define PF530X_SW1_STBY_VOLT 0x0e |
| 33 | #define PF530X_SW1_CTRL1 0x0f |
| 34 | #define PF530X_SW1_CTRL2 0x10 |
| 35 | #define PF530X_CLK_CTRL 0x11 |
| 36 | #define PF530X_SEQ_CTRL1 0x12 |
| 37 | #define PF530X_SEQ_CTRL2 0x13 |
| 38 | #define PF530X_RANDOM_CHK 0x14 |
| 39 | #define PF530X_RANDOM_GEN 0x15 |
| 40 | #define PF530X_WD_CTRL1 0x16 |
| 41 | #define PF530X_WD_SEED 0x17 |
| 42 | #define PF530X_WD_ANSWER 0x18 |
| 43 | #define PF530X_FLT_CNT1 0x19 |
| 44 | #define PF530X_FLT_CNT2 0x1a |
| 45 | #define PF530X_OTP_MODE 0x2f |
| 46 | |
| 47 | enum pf530x_states { |
| 48 | PF530X_STATE_POF, |
| 49 | PF530X_STATE_FUSE_LOAD, |
| 50 | PF530X_STATE_LP_OFF, |
| 51 | PF530X_STATE_SELF_TEST, |
| 52 | PF530X_STATE_POWER_UP, |
| 53 | PF530X_STATE_INIT, |
| 54 | PF530X_STATE_IO_RELEASE, |
| 55 | PF530X_STATE_RUN, |
| 56 | PF530X_STATE_STANDBY, |
| 57 | PF530X_STATE_FAULT, |
| 58 | PF530X_STATE_FAILSAFE, |
| 59 | PF530X_STATE_POWER_DOWN, |
| 60 | PF530X_STATE_2MS_SELFTEST_RETRY, |
| 61 | PF530X_STATE_OFF_DLY, |
| 62 | }; |
| 63 | |
| 64 | #define PF530_FAM 0x50 |
| 65 | enum pf530x_devid { |
| 66 | PF5300 = 0x3, |
| 67 | PF5301 = 0x4, |
| 68 | PF5302 = 0x5, |
| 69 | }; |
| 70 | |
| 71 | #define PF530x_FAM 0x50 |
| 72 | #define PF530x_DEVICE_FAM_MASK GENMASK(7, 4) |
| 73 | #define PF530x_DEVICE_ID_MASK GENMASK(3, 0) |
| 74 | |
| 75 | #define PF530x_STATE_MASK GENMASK(3, 0) |
| 76 | #define PF530x_STATE_RUN 0x07 |
| 77 | #define PF530x_STATE_STANDBY 0x08 |
| 78 | #define PF530x_STATE_LP_OFF 0x02 |
| 79 | |
| 80 | #define PF530X_OTP_STBY_MODE GENMASK(3, 2) |
| 81 | #define PF530X_OTP_RUN_MODE GENMASK(1, 0) |
| 82 | |
| 83 | #define PF530X_INT_STATUS_OV BIT(1) |
| 84 | #define PF530X_INT_STATUS_UV BIT(2) |
| 85 | #define PF530X_INT_STATUS_ILIM BIT(3) |
| 86 | |
| 87 | #define SW1_ILIM_S BIT(0) |
| 88 | #define VMON_UV_S BIT(1) |
| 89 | #define VMON_OV_S BIT(2) |
| 90 | #define VIN_OVLO_S BIT(3) |
| 91 | #define BG_ERR_S BIT(6) |
| 92 | |
| 93 | #define THERM_155_S BIT(3) |
| 94 | #define THERM_140_S BIT(2) |
| 95 | #define THERM_125_S BIT(1) |
| 96 | #define THERM_110_S BIT(0) |
| 97 | |
| 98 | struct pf530x_chip { |
| 99 | struct regmap *regmap; |
| 100 | struct device *dev; |
| 101 | }; |
| 102 | |
| 103 | static const struct regmap_config pf530x_regmap_config = { |
| 104 | .reg_bits = 8, |
| 105 | .val_bits = 8, |
| 106 | .max_register = PF530X_OTP_MODE, |
| 107 | .cache_type = REGCACHE_MAPLE, |
| 108 | }; |
| 109 | |
| 110 | static int pf530x_get_status(struct regulator_dev *rdev) |
| 111 | { |
| 112 | unsigned int state; |
| 113 | int ret; |
| 114 | |
| 115 | ret = regmap_read(map: rdev->regmap, PF530X_INT_SENSE1, val: &state); |
| 116 | if (ret != 0) |
| 117 | return ret; |
| 118 | |
| 119 | if ((state & (BG_ERR_S | SW1_ILIM_S | VMON_UV_S | VMON_OV_S | VIN_OVLO_S)) |
| 120 | != 0) |
| 121 | return REGULATOR_STATUS_ERROR; |
| 122 | |
| 123 | // no errors, check if what non-error state we're in |
| 124 | ret = regmap_read(map: rdev->regmap, PF530X_STATE, val: &state); |
| 125 | if (ret != 0) |
| 126 | return ret; |
| 127 | |
| 128 | state &= PF530x_STATE_MASK; |
| 129 | |
| 130 | switch (state) { |
| 131 | case PF530x_STATE_RUN: |
| 132 | ret = REGULATOR_STATUS_NORMAL; |
| 133 | break; |
| 134 | case PF530x_STATE_STANDBY: |
| 135 | ret = REGULATOR_STATUS_STANDBY; |
| 136 | break; |
| 137 | case PF530x_STATE_LP_OFF: |
| 138 | ret = REGULATOR_STATUS_OFF; |
| 139 | break; |
| 140 | default: |
| 141 | ret = REGULATOR_STATUS_ERROR; |
| 142 | break; |
| 143 | } |
| 144 | return ret; |
| 145 | } |
| 146 | |
| 147 | static int pf530x_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) |
| 148 | { |
| 149 | unsigned int status; |
| 150 | int ret; |
| 151 | |
| 152 | ret = regmap_read(map: rdev->regmap, PF530X_INT_STATUS1, val: &status); |
| 153 | |
| 154 | if (ret != 0) |
| 155 | return ret; |
| 156 | |
| 157 | *flags = 0; |
| 158 | |
| 159 | if (status & PF530X_INT_STATUS_OV) |
| 160 | *flags |= REGULATOR_ERROR_OVER_VOLTAGE_WARN; |
| 161 | |
| 162 | if (status & PF530X_INT_STATUS_UV) |
| 163 | *flags |= REGULATOR_ERROR_UNDER_VOLTAGE; |
| 164 | |
| 165 | if (status & PF530X_INT_STATUS_ILIM) |
| 166 | *flags |= REGULATOR_ERROR_OVER_CURRENT; |
| 167 | |
| 168 | ret = regmap_read(map: rdev->regmap, PF530X_INT_SENSE2, val: &status); |
| 169 | |
| 170 | if (ret != 0) |
| 171 | return ret; |
| 172 | |
| 173 | if ((status & (THERM_155_S | |
| 174 | THERM_140_S | |
| 175 | THERM_125_S | |
| 176 | THERM_110_S)) != 0) |
| 177 | *flags |= REGULATOR_ERROR_OVER_TEMP_WARN; |
| 178 | |
| 179 | return 0; |
| 180 | } |
| 181 | |
| 182 | static const struct regulator_ops pf530x_regulator_ops = { |
| 183 | .enable = regulator_enable_regmap, |
| 184 | .disable = regulator_disable_regmap, |
| 185 | .is_enabled = regulator_is_enabled_regmap, |
| 186 | .map_voltage = regulator_map_voltage_linear_range, |
| 187 | .list_voltage = regulator_list_voltage_linear_range, |
| 188 | .set_voltage_sel = regulator_set_voltage_sel_regmap, |
| 189 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
| 190 | .get_status = pf530x_get_status, |
| 191 | .get_error_flags = pf530x_get_error_flags, |
| 192 | .set_bypass = regulator_set_bypass_regmap, |
| 193 | .get_bypass = regulator_get_bypass_regmap, |
| 194 | }; |
| 195 | |
| 196 | static const struct linear_range vrange = REGULATOR_LINEAR_RANGE(500000, 0, 140, 5000); |
| 197 | |
| 198 | static const struct regulator_desc pf530x_reg_desc = { |
| 199 | .name = "SW1" , |
| 200 | .ops = &pf530x_regulator_ops, |
| 201 | .linear_ranges = &vrange, |
| 202 | .n_linear_ranges = 1, |
| 203 | .type = REGULATOR_VOLTAGE, |
| 204 | .id = 0, |
| 205 | .owner = THIS_MODULE, |
| 206 | .vsel_reg = PF530X_SW1_VOLT, |
| 207 | .vsel_mask = 0xFF, |
| 208 | .bypass_reg = PF530X_SW1_CTRL2, |
| 209 | .bypass_mask = 0x07, |
| 210 | .bypass_val_on = 0x07, |
| 211 | .bypass_val_off = 0x00, |
| 212 | .enable_reg = PF530X_SW1_CTRL1, |
| 213 | .enable_mask = GENMASK(5, 2), |
| 214 | .enable_val = GENMASK(5, 2), |
| 215 | .disable_val = 0, |
| 216 | }; |
| 217 | |
| 218 | static int pf530x_identify(struct pf530x_chip *chip) |
| 219 | { |
| 220 | unsigned int value; |
| 221 | u8 dev_fam, dev_id, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev; |
| 222 | const char *name = NULL; |
| 223 | int ret; |
| 224 | |
| 225 | ret = regmap_read(map: chip->regmap, PF530X_DEVICEID, val: &value); |
| 226 | if (ret) { |
| 227 | dev_err(chip->dev, "failed to read chip family\n" ); |
| 228 | return ret; |
| 229 | } |
| 230 | |
| 231 | dev_fam = value & PF530x_DEVICE_FAM_MASK; |
| 232 | switch (dev_fam) { |
| 233 | case PF530x_FAM: |
| 234 | break; |
| 235 | default: |
| 236 | dev_err(chip->dev, |
| 237 | "Chip 0x%x is not from PF530X family\n" , dev_fam); |
| 238 | return ret; |
| 239 | } |
| 240 | |
| 241 | dev_id = value & PF530x_DEVICE_ID_MASK; |
| 242 | switch (dev_id) { |
| 243 | case PF5300: |
| 244 | name = "PF5300" ; |
| 245 | break; |
| 246 | case PF5301: |
| 247 | name = "PF5301" ; |
| 248 | break; |
| 249 | case PF5302: |
| 250 | name = "PF5302" ; |
| 251 | break; |
| 252 | default: |
| 253 | dev_err(chip->dev, "Unknown pf530x device id 0x%x\n" , dev_id); |
| 254 | return -ENODEV; |
| 255 | } |
| 256 | |
| 257 | ret = regmap_read(map: chip->regmap, PF530X_REV, val: &value); |
| 258 | if (ret) { |
| 259 | dev_err(chip->dev, "failed to read chip rev\n" ); |
| 260 | return ret; |
| 261 | } |
| 262 | |
| 263 | full_layer_rev = ((value & 0xF0) == 0) ? '0' : ((((value & 0xF0) >> 4) - 1) + 'A'); |
| 264 | metal_layer_rev = value & 0xF; |
| 265 | |
| 266 | ret = regmap_read(map: chip->regmap, PF530X_EMREV, val: &value); |
| 267 | if (ret) { |
| 268 | dev_err(chip->dev, "failed to read chip emrev register\n" ); |
| 269 | return ret; |
| 270 | } |
| 271 | |
| 272 | prog_idh = (value >> 4) + 'A'; |
| 273 | // prog_idh skips 'O', per page 96 of the datasheet |
| 274 | if (prog_idh >= 'O') |
| 275 | prog_idh += 1; |
| 276 | |
| 277 | emrev = value & 0x7; |
| 278 | |
| 279 | ret = regmap_read(map: chip->regmap, PF530X_PROGID, val: &value); |
| 280 | if (ret) { |
| 281 | dev_err(chip->dev, "failed to read chip progid register\n" ); |
| 282 | return ret; |
| 283 | } |
| 284 | |
| 285 | if (value >= 0x22) { |
| 286 | dev_err(chip->dev, "invalid value for progid register\n" ); |
| 287 | return -ENODEV; |
| 288 | } else if (value < 10) { |
| 289 | prog_idl = value + '0'; |
| 290 | } else { |
| 291 | prog_idl = (value - 10) + 'A'; |
| 292 | // prog_idh skips 'O', per page 97 of the datasheet |
| 293 | if (prog_idl >= 'O') |
| 294 | prog_idl += 1; |
| 295 | } |
| 296 | |
| 297 | dev_info(chip->dev, "%s Regulator found (Rev %c%d ProgID %c%c EMREV %x).\n" , |
| 298 | name, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev); |
| 299 | |
| 300 | return 0; |
| 301 | } |
| 302 | |
| 303 | static int pf530x_i2c_probe(struct i2c_client *client) |
| 304 | { |
| 305 | struct regulator_config config = { NULL, }; |
| 306 | struct pf530x_chip *chip; |
| 307 | int ret; |
| 308 | struct regulator_dev *rdev; |
| 309 | struct regulator_init_data *init_data; |
| 310 | |
| 311 | chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL); |
| 312 | if (!chip) |
| 313 | return -ENOMEM; |
| 314 | |
| 315 | i2c_set_clientdata(client, data: chip); |
| 316 | chip->dev = &client->dev; |
| 317 | |
| 318 | chip->regmap = devm_regmap_init_i2c(client, &pf530x_regmap_config); |
| 319 | if (IS_ERR(ptr: chip->regmap)) { |
| 320 | ret = PTR_ERR(ptr: chip->regmap); |
| 321 | dev_err(&client->dev, |
| 322 | "regmap allocation failed with err %d\n" , ret); |
| 323 | return ret; |
| 324 | } |
| 325 | |
| 326 | ret = pf530x_identify(chip); |
| 327 | if (ret) |
| 328 | return ret; |
| 329 | |
| 330 | init_data = of_get_regulator_init_data(dev: chip->dev, node: chip->dev->of_node, desc: &pf530x_reg_desc); |
| 331 | if (!init_data) |
| 332 | return -ENODATA; |
| 333 | |
| 334 | config.dev = chip->dev; |
| 335 | config.of_node = chip->dev->of_node; |
| 336 | config.regmap = chip->regmap; |
| 337 | config.init_data = init_data; |
| 338 | |
| 339 | // the config parameter gets copied, it's ok to pass a pointer on the stack here |
| 340 | rdev = devm_regulator_register(dev: &client->dev, regulator_desc: &pf530x_reg_desc, config: &config); |
| 341 | if (IS_ERR(ptr: rdev)) { |
| 342 | dev_err(&client->dev, "failed to register %s regulator\n" , pf530x_reg_desc.name); |
| 343 | return PTR_ERR(ptr: rdev); |
| 344 | } |
| 345 | |
| 346 | return 0; |
| 347 | } |
| 348 | |
| 349 | static const struct of_device_id pf530x_dt_ids[] = { |
| 350 | { .compatible = "nxp,pf5300" ,}, |
| 351 | { } |
| 352 | }; |
| 353 | MODULE_DEVICE_TABLE(of, pf530x_dt_ids); |
| 354 | |
| 355 | static const struct i2c_device_id pf530x_i2c_id[] = { |
| 356 | { "pf5300" , 0 }, |
| 357 | { "pf5301" , 0 }, |
| 358 | { "pf5302" , 0 }, |
| 359 | {}, |
| 360 | }; |
| 361 | MODULE_DEVICE_TABLE(i2c, pf530x_i2c_id); |
| 362 | |
| 363 | static struct i2c_driver pf530x_regulator_driver = { |
| 364 | .id_table = pf530x_i2c_id, |
| 365 | .driver = { |
| 366 | .name = "pf530x" , |
| 367 | .of_match_table = pf530x_dt_ids, |
| 368 | }, |
| 369 | .probe = pf530x_i2c_probe, |
| 370 | }; |
| 371 | module_i2c_driver(pf530x_regulator_driver); |
| 372 | |
| 373 | MODULE_AUTHOR("Woodrow Douglass <wdouglass@carnegierobotics.com>" ); |
| 374 | MODULE_DESCRIPTION("Regulator Driver for NXP's PF5300/PF5301/PF5302 PMIC" ); |
| 375 | MODULE_LICENSE("GPL" ); |
| 376 | |