Svyatoslav Ryhel | 2f98a67 | 2025-03-17 20:49:22 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> |
| 4 | */ |
| 5 | |
| 6 | #include <dm.h> |
| 7 | #include <power/pmic.h> |
| 8 | #include <power/regulator.h> |
| 9 | #include <power/cpcap.h> |
| 10 | #include <linux/delay.h> |
| 11 | #include <linux/err.h> |
| 12 | |
| 13 | /* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */ |
| 14 | #define CPCAP_BIT_VSDIO_SEL BIT(15) |
| 15 | #define CPCAP_BIT_VDIG_SEL BIT(14) |
| 16 | #define CPCAP_BIT_VCAM_SEL BIT(13) |
| 17 | #define CPCAP_BIT_SW6_SEL BIT(12) |
| 18 | #define CPCAP_BIT_SW5_SEL BIT(11) |
| 19 | #define CPCAP_BIT_SW4_SEL BIT(10) |
| 20 | #define CPCAP_BIT_SW3_SEL BIT(9) |
| 21 | #define CPCAP_BIT_SW2_SEL BIT(8) |
| 22 | #define CPCAP_BIT_SW1_SEL BIT(7) |
| 23 | |
| 24 | /* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */ |
| 25 | #define CPCAP_BIT_VUSBINT2_SEL BIT(15) |
| 26 | #define CPCAP_BIT_VUSBINT1_SEL BIT(14) |
| 27 | #define CPCAP_BIT_VVIB_SEL BIT(13) |
| 28 | #define CPCAP_BIT_VWLAN1_SEL BIT(12) |
| 29 | #define CPCAP_BIT_VRF1_SEL BIT(11) |
| 30 | #define CPCAP_BIT_VHVIO_SEL BIT(10) |
| 31 | #define CPCAP_BIT_VDAC_SEL BIT(9) |
| 32 | #define CPCAP_BIT_VUSB_SEL BIT(8) |
| 33 | #define CPCAP_BIT_VSIM_SEL BIT(7) |
| 34 | #define CPCAP_BIT_VRFREF_SEL BIT(6) |
| 35 | #define CPCAP_BIT_VPLL_SEL BIT(5) |
| 36 | #define CPCAP_BIT_VFUSE_SEL BIT(4) |
| 37 | #define CPCAP_BIT_VCSI_SEL BIT(3) |
| 38 | #define CPCAP_BIT_SPARE_14_2 BIT(2) |
| 39 | #define CPCAP_BIT_VWLAN2_SEL BIT(1) |
| 40 | #define CPCAP_BIT_VRF2_SEL BIT(0) |
| 41 | #define CPCAP_BIT_NONE 0 |
| 42 | |
| 43 | /* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */ |
| 44 | #define CPCAP_BIT_VAUDIO_SEL BIT(0) |
| 45 | |
| 46 | /* |
| 47 | * Off mode configuration bit. Used currently only by SW5 on omap4. There's |
| 48 | * the following comment in Motorola Linux kernel tree for it: |
| 49 | * |
| 50 | * When set in the regulator mode, the regulator assignment will be changed |
| 51 | * to secondary when the regulator is disabled. The mode will be set back to |
| 52 | * primary when the regulator is turned on. |
| 53 | */ |
| 54 | #define CPCAP_REG_OFF_MODE_SEC BIT(15) |
| 55 | |
| 56 | #define CPCAP_REG(_reg, _assignment_reg, _assignment_mask, _mode_mask, \ |
| 57 | _volt_mask, _volt_shft, _mode_val, _off_mode_val, _val_tbl, \ |
| 58 | _mode_cntr, _volt_trans_time, _turn_on_time, _bit_offset) { \ |
| 59 | .reg = CPCAP_REG_##_reg, \ |
| 60 | .assignment_reg = CPCAP_REG_##_assignment_reg, \ |
| 61 | .assignment_mask = CPCAP_BIT_##_assignment_mask, \ |
| 62 | .mode_mask = _mode_mask, \ |
| 63 | .volt_mask = _volt_mask, \ |
| 64 | .volt_shft = _volt_shft, \ |
| 65 | .mode_val = _mode_val, \ |
| 66 | .off_mode_val = _off_mode_val, \ |
| 67 | .val_tbl_sz = ARRAY_SIZE(_val_tbl), \ |
| 68 | .val_tbl = _val_tbl, \ |
| 69 | .mode_cntr = _mode_cntr, \ |
| 70 | .volt_trans_time = _volt_trans_time, \ |
| 71 | .turn_on_time = _turn_on_time, \ |
| 72 | .bit_offset_from_cpcap_lowest_voltage = _bit_offset, \ |
| 73 | } |
| 74 | |
| 75 | static const struct cpcap_regulator_data tegra20_regulators[CPCAP_REGULATORS_COUNT] = { |
| 76 | /* BUCK */ |
| 77 | [CPCAP_SW1] = CPCAP_REG(S1C1, ASSIGN2, SW1_SEL, 0x6f00, 0x007f, |
| 78 | 0, 0x6800, 0, sw1_val_tbl, 0, 0, 1500, 0x0c), |
| 79 | [CPCAP_SW2] = CPCAP_REG(S2C1, ASSIGN2, SW2_SEL, 0x6f00, 0x007f, |
| 80 | 0, 0x4804, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), |
| 81 | [CPCAP_SW3] = CPCAP_REG(S3C, ASSIGN2, SW3_SEL, 0x0578, 0x0003, |
| 82 | 0, 0x043c, 0, sw3_val_tbl, 0, 0, 0, 0), |
| 83 | [CPCAP_SW4] = CPCAP_REG(S4C1, ASSIGN2, SW4_SEL, 0x6f00, 0x007f, |
| 84 | 0, 0x4909, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), |
| 85 | [CPCAP_SW5] = CPCAP_REG(S5C, ASSIGN2, SW5_SEL, 0x0028, 0x0000, |
| 86 | 0, 0x0020, 0, sw5_val_tbl, 0, 0, 1500, 0), |
| 87 | [CPCAP_SW6] = CPCAP_REG(S6C, ASSIGN2, SW6_SEL, 0x0000, 0x0000, |
| 88 | 0, 0, 0, unknown_val_tbl, 0, 0, 0, 0), |
| 89 | /* LDO */ |
| 90 | [CPCAP_VCAM] = CPCAP_REG(VCAMC, ASSIGN2, VCAM_SEL, 0x0087, 0x0030, |
| 91 | 4, 0x7, 0, vcam_val_tbl, 0, 420, 1000, 0), |
| 92 | [CPCAP_VCSI] = CPCAP_REG(VCSIC, ASSIGN3, VCSI_SEL, 0x0047, 0x0010, |
| 93 | 4, 0x7, 0, vcsi_val_tbl, 0, 350, 1000, 0), |
| 94 | [CPCAP_VDAC] = CPCAP_REG(VDACC, ASSIGN3, VDAC_SEL, 0x0087, 0x0030, |
| 95 | 4, 0x0, 0, vdac_val_tbl, 0, 420, 1000, 0), |
| 96 | [CPCAP_VDIG] = CPCAP_REG(VDIGC, ASSIGN2, VDIG_SEL, 0x0087, 0x0030, |
| 97 | 4, 0x0, 0, vdig_val_tbl, 0, 420, 1000, 0), |
| 98 | [CPCAP_VFUSE] = CPCAP_REG(VFUSEC, ASSIGN3, VFUSE_SEL, 0x00a0, 0x000f, |
| 99 | 0, 0x0, 0, vfuse_val_tbl, 0, 420, 1000, 0), |
| 100 | [CPCAP_VHVIO] = CPCAP_REG(VHVIOC, ASSIGN3, VHVIO_SEL, 0x0017, 0x0000, |
| 101 | 0, 0x2, 0, vhvio_val_tbl, 0, 0, 1000, 0), |
| 102 | [CPCAP_VSDIO] = CPCAP_REG(VSDIOC, ASSIGN2, VSDIO_SEL, 0x0087, 0x0038, |
| 103 | 3, 0x2, 0, vsdio_val_tbl, 0, 420, 1000, 0), |
| 104 | [CPCAP_VPLL] = CPCAP_REG(VPLLC, ASSIGN3, VPLL_SEL, 0x0047, 0x0018, |
| 105 | 3, 0x1, 0, vpll_val_tbl, 0, 420, 100, 0), |
| 106 | [CPCAP_VRF1] = CPCAP_REG(VRF1C, ASSIGN3, VRF1_SEL, 0x00ac, 0x0002, |
| 107 | 1, 0x0, 0, vrf1_val_tbl, 0, 10, 1000, 0), |
| 108 | [CPCAP_VRF2] = CPCAP_REG(VRF2C, ASSIGN3, VRF2_SEL, 0x0023, 0x0008, |
| 109 | 3, 0x0, 0, vrf2_val_tbl, 0, 10, 1000, 0), |
| 110 | [CPCAP_VRFREF] = CPCAP_REG(VRFREFC, ASSIGN3, VRFREF_SEL, 0x0023, 0x0008, |
| 111 | 3, 0x0, 0, vrfref_val_tbl, 0, 420, 100, 0), |
| 112 | [CPCAP_VWLAN1] = CPCAP_REG(VWLAN1C, ASSIGN3, VWLAN1_SEL, 0x0047, 0x0010, |
| 113 | 4, 0x0, 0, vwlan1_val_tbl, 0, 420, 1000, 0), |
| 114 | [CPCAP_VWLAN2] = CPCAP_REG(VWLAN2C, ASSIGN3, VWLAN2_SEL, 0x020c, 0x00c0, |
| 115 | 6, 0xd, 0, vwlan2_val_tbl, 0, 420, 1000, 0), |
| 116 | [CPCAP_VSIM] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x0023, 0x0008, |
| 117 | 3, 0x0, 0, vsim_val_tbl, 0, 420, 1000, 0), |
| 118 | [CPCAP_VSIMCARD] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x1e80, 0x0008, |
| 119 | 3, 0x1E00, 0, vsimcard_val_tbl, 0, 420, 1000, 0), |
| 120 | [CPCAP_VVIB] = CPCAP_REG(VVIBC, ASSIGN3, VVIB_SEL, 0x0001, 0x000c, |
| 121 | 2, 0x1, 0, vvib_val_tbl, 0, 500, 500, 0), |
| 122 | [CPCAP_VUSB] = CPCAP_REG(VUSBC, ASSIGN3, VUSB_SEL, 0x011c, 0x0040, |
| 123 | 6, 0xc, 0, vusb_val_tbl, 0, 0, 1000, 0), |
| 124 | [CPCAP_VAUDIO] = CPCAP_REG(VAUDIOC, ASSIGN4, VAUDIO_SEL, 0x0016, 0x0001, |
| 125 | 0, 0x5, 0, vaudio_val_tbl, 0, 0, 1000, 0), |
| 126 | }; |
| 127 | |
| 128 | static int cpcap_regulator_get_value(struct udevice *dev) |
| 129 | { |
| 130 | const struct cpcap_regulator_data *regulator = |
| 131 | &tegra20_regulators[dev->driver_data]; |
| 132 | int value, volt_shift = regulator->volt_shft; |
| 133 | |
| 134 | value = pmic_reg_read(dev->parent, regulator->reg); |
| 135 | if (value < 0) |
| 136 | return value; |
| 137 | |
| 138 | if (!(value & regulator->mode_mask)) |
| 139 | return 0; |
| 140 | |
| 141 | value &= regulator->volt_mask; |
| 142 | value -= regulator->bit_offset_from_cpcap_lowest_voltage; |
| 143 | |
| 144 | return regulator->val_tbl[value >> volt_shift]; |
| 145 | } |
| 146 | |
| 147 | static int cpcap_regulator_set_value(struct udevice *dev, int uV) |
| 148 | { |
| 149 | const struct cpcap_regulator_data *regulator = |
| 150 | &tegra20_regulators[dev->driver_data]; |
| 151 | int value, ret, volt_shift = regulator->volt_shft; |
| 152 | |
| 153 | if (dev->driver_data == CPCAP_VRF1) { |
| 154 | if (uV > 2500000) |
| 155 | value = 0; |
| 156 | else |
| 157 | value = regulator->volt_mask; |
| 158 | } else { |
| 159 | for (value = 0; value < regulator->val_tbl_sz; value++) |
| 160 | if (regulator->val_tbl[value] >= uV) |
| 161 | break; |
| 162 | |
| 163 | if (value >= regulator->val_tbl_sz) |
| 164 | value = regulator->val_tbl_sz; |
| 165 | |
| 166 | value <<= volt_shift; |
| 167 | value += regulator->bit_offset_from_cpcap_lowest_voltage; |
| 168 | } |
| 169 | |
| 170 | ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->volt_mask, |
| 171 | value); |
| 172 | if (ret) |
| 173 | return ret; |
| 174 | |
| 175 | if (regulator->volt_trans_time) |
| 176 | udelay(regulator->volt_trans_time); |
| 177 | |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | static int cpcap_regulator_get_enable(struct udevice *dev) |
| 182 | { |
| 183 | const struct cpcap_regulator_data *regulator = |
| 184 | &tegra20_regulators[dev->driver_data]; |
| 185 | int value; |
| 186 | |
| 187 | value = pmic_reg_read(dev->parent, regulator->reg); |
| 188 | if (value < 0) |
| 189 | return value; |
| 190 | |
| 191 | return (value & regulator->mode_mask) ? 1 : 0; |
| 192 | } |
| 193 | |
| 194 | static int cpcap_regulator_set_enable(struct udevice *dev, bool enable) |
| 195 | { |
| 196 | const struct cpcap_regulator_data *regulator = |
| 197 | &tegra20_regulators[dev->driver_data]; |
| 198 | int ret; |
| 199 | |
| 200 | if (enable) { |
| 201 | ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, |
| 202 | regulator->mode_val); |
| 203 | if (ret) |
| 204 | return ret; |
| 205 | } |
| 206 | |
| 207 | if (regulator->mode_val & CPCAP_REG_OFF_MODE_SEC) { |
| 208 | ret = pmic_clrsetbits(dev->parent, regulator->assignment_reg, |
| 209 | regulator->assignment_mask, |
| 210 | enable ? 0 : regulator->assignment_mask); |
| 211 | if (ret) |
| 212 | return ret; |
| 213 | } |
| 214 | |
| 215 | if (!enable) { |
| 216 | ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, |
| 217 | regulator->off_mode_val); |
| 218 | if (ret) |
| 219 | return ret; |
| 220 | } |
| 221 | |
| 222 | if (regulator->turn_on_time) |
| 223 | udelay(regulator->turn_on_time); |
| 224 | |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | static int cpcap_regulator_probe(struct udevice *dev) |
| 229 | { |
| 230 | struct dm_regulator_uclass_plat *uc_pdata = dev_get_uclass_plat(dev); |
| 231 | int id; |
| 232 | |
| 233 | for (id = 0; id < CPCAP_REGULATORS_COUNT; id++) |
| 234 | if (cpcap_regulator_to_name[id]) |
| 235 | if (!strcmp(dev->name, cpcap_regulator_to_name[id])) |
| 236 | break; |
| 237 | |
| 238 | switch (id) { |
| 239 | case CPCAP_SW1 ... CPCAP_SW6: |
| 240 | uc_pdata->type = REGULATOR_TYPE_BUCK; |
| 241 | break; |
| 242 | |
| 243 | case CPCAP_VCAM ... CPCAP_VAUDIO: |
| 244 | uc_pdata->type = REGULATOR_TYPE_LDO; |
| 245 | break; |
| 246 | |
| 247 | default: |
| 248 | log_err("CPCAP: Invalid regulator ID\n"); |
| 249 | return -ENODEV; |
| 250 | } |
| 251 | |
| 252 | dev->driver_data = id; |
| 253 | return 0; |
| 254 | } |
| 255 | |
| 256 | static const struct dm_regulator_ops cpcap_regulator_ops = { |
| 257 | .get_value = cpcap_regulator_get_value, |
| 258 | .set_value = cpcap_regulator_set_value, |
| 259 | .get_enable = cpcap_regulator_get_enable, |
| 260 | .set_enable = cpcap_regulator_set_enable, |
| 261 | }; |
| 262 | |
| 263 | U_BOOT_DRIVER(cpcap_sw) = { |
| 264 | .name = CPCAP_SW_DRIVER, |
| 265 | .id = UCLASS_REGULATOR, |
| 266 | .ops = &cpcap_regulator_ops, |
| 267 | .probe = cpcap_regulator_probe, |
| 268 | }; |
| 269 | |
| 270 | U_BOOT_DRIVER(cpcap_ldo) = { |
| 271 | .name = CPCAP_LDO_DRIVER, |
| 272 | .id = UCLASS_REGULATOR, |
| 273 | .ops = &cpcap_regulator_ops, |
| 274 | .probe = cpcap_regulator_probe, |
| 275 | }; |