Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'ib-mt6360-for-5.15-signed' into psy-next

Immutable branch between regulator and power-supply for for 5.15

This immutable branch introduces the MT6360 charger driver,
which requires a new linear range helper.

Signed-off-by: Sebastian Reichel <sre@kernel.org>

+960
+48
Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/power/supply/mt6360_charger.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Battery charger driver for MT6360 PMIC from MediaTek Integrated. 8 + 9 + maintainers: 10 + - Gene Chen <gene_chen@richtek.com> 11 + 12 + description: | 13 + This module is part of the MT6360 MFD device. 14 + Provides Battery Charger, Boost for OTG devices and BC1.2 detection. 15 + 16 + properties: 17 + compatible: 18 + const: mediatek,mt6360-chg 19 + 20 + richtek,vinovp-microvolt: 21 + description: Maximum CHGIN regulation voltage in uV. 22 + enum: [ 5500000, 6500000, 11000000, 14500000 ] 23 + 24 + 25 + usb-otg-vbus-regulator: 26 + type: object 27 + description: OTG boost regulator. 28 + $ref: /schemas/regulator/regulator.yaml# 29 + 30 + required: 31 + - compatible 32 + 33 + additionalProperties: false 34 + 35 + examples: 36 + - | 37 + mt6360_charger: charger { 38 + compatible = "mediatek,mt6360-chg"; 39 + richtek,vinovp-microvolt = <14500000>; 40 + 41 + otg_vbus_regulator: usb-otg-vbus-regulator { 42 + regulator-compatible = "usb-otg-vbus"; 43 + regulator-name = "usb-otg-vbus"; 44 + regulator-min-microvolt = <4425000>; 45 + regulator-max-microvolt = <5825000>; 46 + }; 47 + }; 48 + ...
+11
drivers/power/supply/Kconfig
··· 577 577 Battery charger. This driver provides Battery charger power management 578 578 functions on the systems. 579 579 580 + config CHARGER_MT6360 581 + tristate "Mediatek MT6360 Charger Driver" 582 + depends on MFD_MT6360 583 + depends on REGULATOR 584 + select LINEAR_RANGES 585 + help 586 + Say Y here to enable MT6360 Charger Part. 587 + The device supports High-Accuracy Voltage/Current Regulation, 588 + Average Input Current Regulation, Battery Temperature Sensing, 589 + Over-Temperature Protection, DPDM Detection for BC1.2. 590 + 580 591 config CHARGER_QCOM_SMBB 581 592 tristate "Qualcomm Switch-Mode Battery Charger and Boost" 582 593 depends on MFD_SPMI_PMIC || COMPILE_TEST
+1
drivers/power/supply/Makefile
··· 78 78 obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o 79 79 obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o 80 80 obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o 81 + obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o 81 82 obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o 82 83 obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o 83 84 obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
+867
drivers/power/supply/mt6360_charger.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2021 MediaTek Inc. 4 + */ 5 + 6 + #include <linux/devm-helpers.h> 7 + #include <linux/init.h> 8 + #include <linux/interrupt.h> 9 + #include <linux/kernel.h> 10 + #include <linux/linear_range.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/power_supply.h> 15 + #include <linux/property.h> 16 + #include <linux/regmap.h> 17 + #include <linux/regulator/driver.h> 18 + 19 + #define MT6360_PMU_CHG_CTRL1 0x311 20 + #define MT6360_PMU_CHG_CTRL2 0x312 21 + #define MT6360_PMU_CHG_CTRL3 0x313 22 + #define MT6360_PMU_CHG_CTRL4 0x314 23 + #define MT6360_PMU_CHG_CTRL5 0x315 24 + #define MT6360_PMU_CHG_CTRL6 0x316 25 + #define MT6360_PMU_CHG_CTRL7 0x317 26 + #define MT6360_PMU_CHG_CTRL8 0x318 27 + #define MT6360_PMU_CHG_CTRL9 0x319 28 + #define MT6360_PMU_CHG_CTRL10 0x31A 29 + #define MT6360_PMU_DEVICE_TYPE 0x322 30 + #define MT6360_PMU_USB_STATUS1 0x327 31 + #define MT6360_PMU_CHG_STAT 0x34A 32 + #define MT6360_PMU_CHG_CTRL19 0x361 33 + #define MT6360_PMU_FOD_STAT 0x3E7 34 + 35 + /* MT6360_PMU_CHG_CTRL1 */ 36 + #define MT6360_FSLP_SHFT (3) 37 + #define MT6360_FSLP_MASK BIT(MT6360_FSLP_SHFT) 38 + #define MT6360_OPA_MODE_SHFT (0) 39 + #define MT6360_OPA_MODE_MASK BIT(MT6360_OPA_MODE_SHFT) 40 + /* MT6360_PMU_CHG_CTRL2 */ 41 + #define MT6360_IINLMTSEL_SHFT (2) 42 + #define MT6360_IINLMTSEL_MASK GENMASK(3, 2) 43 + /* MT6360_PMU_CHG_CTRL3 */ 44 + #define MT6360_IAICR_SHFT (2) 45 + #define MT6360_IAICR_MASK GENMASK(7, 2) 46 + #define MT6360_ILIM_EN_MASK BIT(0) 47 + /* MT6360_PMU_CHG_CTRL4 */ 48 + #define MT6360_VOREG_SHFT (1) 49 + #define MT6360_VOREG_MASK GENMASK(7, 1) 50 + /* MT6360_PMU_CHG_CTRL5 */ 51 + #define MT6360_VOBST_MASK GENMASK(7, 2) 52 + /* MT6360_PMU_CHG_CTRL6 */ 53 + #define MT6360_VMIVR_SHFT (1) 54 + #define MT6360_VMIVR_MASK GENMASK(7, 1) 55 + /* MT6360_PMU_CHG_CTRL7 */ 56 + #define MT6360_ICHG_SHFT (2) 57 + #define MT6360_ICHG_MASK GENMASK(7, 2) 58 + /* MT6360_PMU_CHG_CTRL8 */ 59 + #define MT6360_IPREC_SHFT (0) 60 + #define MT6360_IPREC_MASK GENMASK(3, 0) 61 + /* MT6360_PMU_CHG_CTRL9 */ 62 + #define MT6360_IEOC_SHFT (4) 63 + #define MT6360_IEOC_MASK GENMASK(7, 4) 64 + /* MT6360_PMU_CHG_CTRL10 */ 65 + #define MT6360_OTG_OC_MASK GENMASK(3, 0) 66 + /* MT6360_PMU_DEVICE_TYPE */ 67 + #define MT6360_USBCHGEN_MASK BIT(7) 68 + /* MT6360_PMU_USB_STATUS1 */ 69 + #define MT6360_USB_STATUS_SHFT (4) 70 + #define MT6360_USB_STATUS_MASK GENMASK(6, 4) 71 + /* MT6360_PMU_CHG_STAT */ 72 + #define MT6360_CHG_STAT_SHFT (6) 73 + #define MT6360_CHG_STAT_MASK GENMASK(7, 6) 74 + #define MT6360_VBAT_LVL_MASK BIT(5) 75 + /* MT6360_PMU_CHG_CTRL19 */ 76 + #define MT6360_VINOVP_SHFT (5) 77 + #define MT6360_VINOVP_MASK GENMASK(6, 5) 78 + /* MT6360_PMU_FOD_STAT */ 79 + #define MT6360_CHRDET_EXT_MASK BIT(4) 80 + 81 + /* uV */ 82 + #define MT6360_VMIVR_MIN 3900000 83 + #define MT6360_VMIVR_MAX 13400000 84 + #define MT6360_VMIVR_STEP 100000 85 + /* uA */ 86 + #define MT6360_ICHG_MIN 100000 87 + #define MT6360_ICHG_MAX 5000000 88 + #define MT6360_ICHG_STEP 100000 89 + /* uV */ 90 + #define MT6360_VOREG_MIN 3900000 91 + #define MT6360_VOREG_MAX 4710000 92 + #define MT6360_VOREG_STEP 10000 93 + /* uA */ 94 + #define MT6360_AICR_MIN 100000 95 + #define MT6360_AICR_MAX 3250000 96 + #define MT6360_AICR_STEP 50000 97 + /* uA */ 98 + #define MT6360_IPREC_MIN 100000 99 + #define MT6360_IPREC_MAX 850000 100 + #define MT6360_IPREC_STEP 50000 101 + /* uA */ 102 + #define MT6360_IEOC_MIN 100000 103 + #define MT6360_IEOC_MAX 850000 104 + #define MT6360_IEOC_STEP 50000 105 + 106 + enum { 107 + MT6360_RANGE_VMIVR, 108 + MT6360_RANGE_ICHG, 109 + MT6360_RANGE_VOREG, 110 + MT6360_RANGE_AICR, 111 + MT6360_RANGE_IPREC, 112 + MT6360_RANGE_IEOC, 113 + MT6360_RANGE_MAX, 114 + }; 115 + 116 + #define MT6360_LINEAR_RANGE(idx, _min, _min_sel, _max_sel, _step) \ 117 + [idx] = REGULATOR_LINEAR_RANGE(_min, _min_sel, _max_sel, _step) 118 + 119 + static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = { 120 + MT6360_LINEAR_RANGE(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000), 121 + MT6360_LINEAR_RANGE(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000), 122 + MT6360_LINEAR_RANGE(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000), 123 + MT6360_LINEAR_RANGE(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000), 124 + MT6360_LINEAR_RANGE(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000), 125 + MT6360_LINEAR_RANGE(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000), 126 + }; 127 + 128 + struct mt6360_chg_info { 129 + struct device *dev; 130 + struct regmap *regmap; 131 + struct power_supply_desc psy_desc; 132 + struct power_supply *psy; 133 + struct regulator_dev *otg_rdev; 134 + struct mutex chgdet_lock; 135 + u32 vinovp; 136 + bool pwr_rdy; 137 + bool bc12_en; 138 + int psy_usb_type; 139 + struct work_struct chrdet_work; 140 + }; 141 + 142 + enum mt6360_iinlmtsel { 143 + MT6360_IINLMTSEL_AICR_3250 = 0, 144 + MT6360_IINLMTSEL_CHG_TYPE, 145 + MT6360_IINLMTSEL_AICR, 146 + MT6360_IINLMTSEL_LOWER_LEVEL, 147 + }; 148 + 149 + enum mt6360_pmu_chg_type { 150 + MT6360_CHG_TYPE_NOVBUS = 0, 151 + MT6360_CHG_TYPE_UNDER_GOING, 152 + MT6360_CHG_TYPE_SDP, 153 + MT6360_CHG_TYPE_SDPNSTD, 154 + MT6360_CHG_TYPE_DCP, 155 + MT6360_CHG_TYPE_CDP, 156 + MT6360_CHG_TYPE_DISABLE_BC12, 157 + MT6360_CHG_TYPE_MAX, 158 + }; 159 + 160 + static enum power_supply_usb_type mt6360_charger_usb_types[] = { 161 + POWER_SUPPLY_USB_TYPE_UNKNOWN, 162 + POWER_SUPPLY_USB_TYPE_SDP, 163 + POWER_SUPPLY_USB_TYPE_DCP, 164 + POWER_SUPPLY_USB_TYPE_CDP, 165 + }; 166 + 167 + static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci, 168 + bool *pwr_rdy) 169 + { 170 + int ret; 171 + unsigned int regval; 172 + 173 + ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, &regval); 174 + if (ret < 0) 175 + return ret; 176 + *pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false; 177 + return 0; 178 + } 179 + 180 + static int mt6360_charger_get_online(struct mt6360_chg_info *mci, 181 + union power_supply_propval *val) 182 + { 183 + int ret; 184 + bool pwr_rdy; 185 + 186 + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); 187 + if (ret < 0) 188 + return ret; 189 + val->intval = pwr_rdy ? true : false; 190 + return 0; 191 + } 192 + 193 + static int mt6360_charger_get_status(struct mt6360_chg_info *mci, 194 + union power_supply_propval *val) 195 + { 196 + int status, ret; 197 + unsigned int regval; 198 + bool pwr_rdy; 199 + 200 + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); 201 + if (ret < 0) 202 + return ret; 203 + if (!pwr_rdy) { 204 + status = POWER_SUPPLY_STATUS_DISCHARGING; 205 + goto out; 206 + } 207 + 208 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval); 209 + if (ret < 0) 210 + return ret; 211 + regval &= MT6360_CHG_STAT_MASK; 212 + regval >>= MT6360_CHG_STAT_SHFT; 213 + switch (regval) { 214 + case 0x0: 215 + status = POWER_SUPPLY_STATUS_NOT_CHARGING; 216 + break; 217 + case 0x1: 218 + status = POWER_SUPPLY_STATUS_CHARGING; 219 + break; 220 + case 0x2: 221 + status = POWER_SUPPLY_STATUS_FULL; 222 + break; 223 + default: 224 + ret = -EIO; 225 + } 226 + out: 227 + if (!ret) 228 + val->intval = status; 229 + return ret; 230 + } 231 + 232 + static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci, 233 + union power_supply_propval *val) 234 + { 235 + int type, ret; 236 + unsigned int regval; 237 + u8 chg_stat; 238 + 239 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval); 240 + if (ret < 0) 241 + return ret; 242 + 243 + chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT; 244 + switch (chg_stat) { 245 + case 0x01: /* Charge in Progress */ 246 + if (regval & MT6360_VBAT_LVL_MASK) 247 + type = POWER_SUPPLY_CHARGE_TYPE_FAST; 248 + else 249 + type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 250 + break; 251 + case 0x00: /* Not Charging */ 252 + case 0x02: /* Charge Done */ 253 + case 0x03: /* Charge Fault */ 254 + default: 255 + type = POWER_SUPPLY_CHARGE_TYPE_NONE; 256 + break; 257 + } 258 + 259 + val->intval = type; 260 + return 0; 261 + } 262 + 263 + static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci, 264 + union power_supply_propval *val) 265 + { 266 + int ret; 267 + u32 sel, value; 268 + 269 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel); 270 + if (ret < 0) 271 + return ret; 272 + sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT; 273 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value); 274 + if (!ret) 275 + val->intval = value; 276 + return ret; 277 + } 278 + 279 + static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci, 280 + union power_supply_propval *val) 281 + { 282 + val->intval = MT6360_ICHG_MAX; 283 + return 0; 284 + } 285 + 286 + static int mt6360_charger_get_cv(struct mt6360_chg_info *mci, 287 + union power_supply_propval *val) 288 + { 289 + int ret; 290 + u32 sel, value; 291 + 292 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel); 293 + if (ret < 0) 294 + return ret; 295 + sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT; 296 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value); 297 + if (!ret) 298 + val->intval = value; 299 + return ret; 300 + } 301 + 302 + static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci, 303 + union power_supply_propval *val) 304 + { 305 + val->intval = MT6360_VOREG_MAX; 306 + return 0; 307 + } 308 + 309 + static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci, 310 + union power_supply_propval *val) 311 + { 312 + int ret; 313 + u32 sel, value; 314 + 315 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel); 316 + if (ret < 0) 317 + return ret; 318 + sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT; 319 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value); 320 + if (!ret) 321 + val->intval = value; 322 + return ret; 323 + } 324 + 325 + static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci, 326 + union power_supply_propval *val) 327 + { 328 + int ret; 329 + u32 sel, value; 330 + 331 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel); 332 + if (ret < 0) 333 + return ret; 334 + sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT; 335 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value); 336 + if (!ret) 337 + val->intval = value; 338 + return ret; 339 + } 340 + 341 + static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci, 342 + union power_supply_propval *val) 343 + { 344 + int ret; 345 + u32 sel, value; 346 + 347 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel); 348 + if (ret < 0) 349 + return ret; 350 + sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT; 351 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value); 352 + if (!ret) 353 + val->intval = value; 354 + return ret; 355 + } 356 + 357 + static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci, 358 + union power_supply_propval *val) 359 + { 360 + int ret; 361 + u32 sel, value; 362 + 363 + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel); 364 + if (ret < 0) 365 + return ret; 366 + sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT; 367 + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value); 368 + if (!ret) 369 + val->intval = value; 370 + return ret; 371 + } 372 + 373 + static int mt6360_charger_set_online(struct mt6360_chg_info *mci, 374 + const union power_supply_propval *val) 375 + { 376 + u8 force_sleep = val->intval ? 0 : 1; 377 + 378 + return regmap_update_bits(mci->regmap, 379 + MT6360_PMU_CHG_CTRL1, 380 + MT6360_FSLP_MASK, 381 + force_sleep << MT6360_FSLP_SHFT); 382 + } 383 + 384 + static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci, 385 + const union power_supply_propval *val) 386 + { 387 + u32 sel; 388 + 389 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel); 390 + return regmap_update_bits(mci->regmap, 391 + MT6360_PMU_CHG_CTRL7, 392 + MT6360_ICHG_MASK, 393 + sel << MT6360_ICHG_SHFT); 394 + } 395 + 396 + static int mt6360_charger_set_cv(struct mt6360_chg_info *mci, 397 + const union power_supply_propval *val) 398 + { 399 + u32 sel; 400 + 401 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel); 402 + return regmap_update_bits(mci->regmap, 403 + MT6360_PMU_CHG_CTRL4, 404 + MT6360_VOREG_MASK, 405 + sel << MT6360_VOREG_SHFT); 406 + } 407 + 408 + static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci, 409 + const union power_supply_propval *val) 410 + { 411 + u32 sel; 412 + 413 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel); 414 + return regmap_update_bits(mci->regmap, 415 + MT6360_PMU_CHG_CTRL3, 416 + MT6360_IAICR_MASK, 417 + sel << MT6360_IAICR_SHFT); 418 + } 419 + 420 + static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci, 421 + const union power_supply_propval *val) 422 + { 423 + u32 sel; 424 + 425 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel); 426 + return regmap_update_bits(mci->regmap, 427 + MT6360_PMU_CHG_CTRL3, 428 + MT6360_VMIVR_MASK, 429 + sel << MT6360_VMIVR_SHFT); 430 + } 431 + 432 + static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci, 433 + const union power_supply_propval *val) 434 + { 435 + u32 sel; 436 + 437 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel); 438 + return regmap_update_bits(mci->regmap, 439 + MT6360_PMU_CHG_CTRL8, 440 + MT6360_IPREC_MASK, 441 + sel << MT6360_IPREC_SHFT); 442 + } 443 + 444 + static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci, 445 + const union power_supply_propval *val) 446 + { 447 + u32 sel; 448 + 449 + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel); 450 + return regmap_update_bits(mci->regmap, 451 + MT6360_PMU_CHG_CTRL9, 452 + MT6360_IEOC_MASK, 453 + sel << MT6360_IEOC_SHFT); 454 + } 455 + 456 + static int mt6360_charger_get_property(struct power_supply *psy, 457 + enum power_supply_property psp, 458 + union power_supply_propval *val) 459 + { 460 + struct mt6360_chg_info *mci = power_supply_get_drvdata(psy); 461 + int ret = 0; 462 + 463 + switch (psp) { 464 + case POWER_SUPPLY_PROP_ONLINE: 465 + ret = mt6360_charger_get_online(mci, val); 466 + break; 467 + case POWER_SUPPLY_PROP_STATUS: 468 + ret = mt6360_charger_get_status(mci, val); 469 + break; 470 + case POWER_SUPPLY_PROP_CHARGE_TYPE: 471 + ret = mt6360_charger_get_charge_type(mci, val); 472 + break; 473 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 474 + ret = mt6360_charger_get_ichg(mci, val); 475 + break; 476 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 477 + ret = mt6360_charger_get_max_ichg(mci, val); 478 + break; 479 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 480 + ret = mt6360_charger_get_cv(mci, val); 481 + break; 482 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 483 + ret = mt6360_charger_get_max_cv(mci, val); 484 + break; 485 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 486 + ret = mt6360_charger_get_aicr(mci, val); 487 + break; 488 + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: 489 + ret = mt6360_charger_get_mivr(mci, val); 490 + break; 491 + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 492 + ret = mt6360_charger_get_iprechg(mci, val); 493 + break; 494 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 495 + ret = mt6360_charger_get_ieoc(mci, val); 496 + break; 497 + case POWER_SUPPLY_PROP_USB_TYPE: 498 + val->intval = mci->psy_usb_type; 499 + break; 500 + default: 501 + ret = -ENODATA; 502 + } 503 + return ret; 504 + } 505 + 506 + static int mt6360_charger_set_property(struct power_supply *psy, 507 + enum power_supply_property psp, 508 + const union power_supply_propval *val) 509 + { 510 + struct mt6360_chg_info *mci = power_supply_get_drvdata(psy); 511 + int ret; 512 + 513 + switch (psp) { 514 + case POWER_SUPPLY_PROP_ONLINE: 515 + ret = mt6360_charger_set_online(mci, val); 516 + break; 517 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 518 + ret = mt6360_charger_set_ichg(mci, val); 519 + break; 520 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 521 + ret = mt6360_charger_set_cv(mci, val); 522 + break; 523 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 524 + ret = mt6360_charger_set_aicr(mci, val); 525 + break; 526 + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: 527 + ret = mt6360_charger_set_mivr(mci, val); 528 + break; 529 + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 530 + ret = mt6360_charger_set_iprechg(mci, val); 531 + break; 532 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 533 + ret = mt6360_charger_set_ieoc(mci, val); 534 + break; 535 + default: 536 + ret = -EINVAL; 537 + } 538 + return ret; 539 + } 540 + 541 + static int mt6360_charger_property_is_writeable(struct power_supply *psy, 542 + enum power_supply_property psp) 543 + { 544 + switch (psp) { 545 + case POWER_SUPPLY_PROP_ONLINE: 546 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 547 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 548 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 549 + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: 550 + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 551 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 552 + return 1; 553 + default: 554 + return 0; 555 + } 556 + } 557 + 558 + static enum power_supply_property mt6360_charger_properties[] = { 559 + POWER_SUPPLY_PROP_ONLINE, 560 + POWER_SUPPLY_PROP_STATUS, 561 + POWER_SUPPLY_PROP_CHARGE_TYPE, 562 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 563 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 564 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 565 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 566 + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 567 + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, 568 + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, 569 + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 570 + POWER_SUPPLY_PROP_USB_TYPE, 571 + }; 572 + 573 + static const struct power_supply_desc mt6360_charger_desc = { 574 + .type = POWER_SUPPLY_TYPE_USB, 575 + .properties = mt6360_charger_properties, 576 + .num_properties = ARRAY_SIZE(mt6360_charger_properties), 577 + .get_property = mt6360_charger_get_property, 578 + .set_property = mt6360_charger_set_property, 579 + .property_is_writeable = mt6360_charger_property_is_writeable, 580 + .usb_types = mt6360_charger_usb_types, 581 + .num_usb_types = ARRAY_SIZE(mt6360_charger_usb_types), 582 + }; 583 + 584 + static const struct regulator_ops mt6360_chg_otg_ops = { 585 + .list_voltage = regulator_list_voltage_linear, 586 + .enable = regulator_enable_regmap, 587 + .disable = regulator_disable_regmap, 588 + .is_enabled = regulator_is_enabled_regmap, 589 + .set_voltage_sel = regulator_set_voltage_sel_regmap, 590 + .get_voltage_sel = regulator_get_voltage_sel_regmap, 591 + }; 592 + 593 + static const struct regulator_desc mt6360_otg_rdesc = { 594 + .of_match = "usb-otg-vbus", 595 + .name = "usb-otg-vbus", 596 + .ops = &mt6360_chg_otg_ops, 597 + .owner = THIS_MODULE, 598 + .type = REGULATOR_VOLTAGE, 599 + .min_uV = 4425000, 600 + .uV_step = 25000, 601 + .n_voltages = 57, 602 + .vsel_reg = MT6360_PMU_CHG_CTRL5, 603 + .vsel_mask = MT6360_VOBST_MASK, 604 + .enable_reg = MT6360_PMU_CHG_CTRL1, 605 + .enable_mask = MT6360_OPA_MODE_MASK, 606 + }; 607 + 608 + static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data) 609 + { 610 + struct mt6360_chg_info *mci = data; 611 + int ret; 612 + unsigned int usb_status; 613 + int last_usb_type; 614 + 615 + mutex_lock(&mci->chgdet_lock); 616 + if (!mci->bc12_en) { 617 + dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n"); 618 + goto out; 619 + } 620 + last_usb_type = mci->psy_usb_type; 621 + /* Plug in */ 622 + ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status); 623 + if (ret < 0) 624 + goto out; 625 + usb_status &= MT6360_USB_STATUS_MASK; 626 + usb_status >>= MT6360_USB_STATUS_SHFT; 627 + switch (usb_status) { 628 + case MT6360_CHG_TYPE_NOVBUS: 629 + dev_dbg(mci->dev, "Received attach interrupt, no vbus\n"); 630 + goto out; 631 + case MT6360_CHG_TYPE_UNDER_GOING: 632 + dev_dbg(mci->dev, "Received attach interrupt, under going...\n"); 633 + goto out; 634 + case MT6360_CHG_TYPE_SDP: 635 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; 636 + break; 637 + case MT6360_CHG_TYPE_SDPNSTD: 638 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; 639 + break; 640 + case MT6360_CHG_TYPE_CDP: 641 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; 642 + break; 643 + case MT6360_CHG_TYPE_DCP: 644 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; 645 + break; 646 + case MT6360_CHG_TYPE_DISABLE_BC12: 647 + dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n"); 648 + goto out; 649 + default: 650 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; 651 + dev_dbg(mci->dev, "Received attach interrupt, reserved address\n"); 652 + goto out; 653 + } 654 + 655 + dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type); 656 + if (last_usb_type != mci->psy_usb_type) 657 + power_supply_changed(mci->psy); 658 + out: 659 + mutex_unlock(&mci->chgdet_lock); 660 + return IRQ_HANDLED; 661 + } 662 + 663 + static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci) 664 + { 665 + int ret; 666 + bool pwr_rdy; 667 + 668 + mutex_lock(&mci->chgdet_lock); 669 + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); 670 + if (ret < 0) 671 + goto out; 672 + if (mci->pwr_rdy == pwr_rdy) { 673 + dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy); 674 + goto out; 675 + } 676 + mci->pwr_rdy = pwr_rdy; 677 + dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy); 678 + if (!pwr_rdy) { 679 + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; 680 + power_supply_changed(mci->psy); 681 + 682 + } 683 + ret = regmap_update_bits(mci->regmap, 684 + MT6360_PMU_DEVICE_TYPE, 685 + MT6360_USBCHGEN_MASK, 686 + pwr_rdy ? MT6360_USBCHGEN_MASK : 0); 687 + if (ret < 0) 688 + goto out; 689 + mci->bc12_en = pwr_rdy; 690 + out: 691 + mutex_unlock(&mci->chgdet_lock); 692 + } 693 + 694 + static void mt6360_chrdet_work(struct work_struct *work) 695 + { 696 + struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of( 697 + work, struct mt6360_chg_info, chrdet_work); 698 + 699 + mt6360_handle_chrdet_ext_evt(mci); 700 + } 701 + 702 + static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data) 703 + { 704 + struct mt6360_chg_info *mci = data; 705 + 706 + mt6360_handle_chrdet_ext_evt(mci); 707 + return IRQ_HANDLED; 708 + } 709 + 710 + static int mt6360_chg_irq_register(struct platform_device *pdev) 711 + { 712 + const struct { 713 + const char *name; 714 + irq_handler_t handler; 715 + } irq_descs[] = { 716 + { "attach_i", mt6360_pmu_attach_i_handler }, 717 + { "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler } 718 + }; 719 + int i, ret; 720 + 721 + for (i = 0; i < ARRAY_SIZE(irq_descs); i++) { 722 + ret = platform_get_irq_byname(pdev, irq_descs[i].name); 723 + if (ret < 0) 724 + return ret; 725 + 726 + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, 727 + irq_descs[i].handler, 728 + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 729 + irq_descs[i].name, 730 + platform_get_drvdata(pdev)); 731 + if (ret < 0) 732 + return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n", 733 + irq_descs[i].name); 734 + } 735 + 736 + return 0; 737 + } 738 + 739 + static u32 mt6360_vinovp_trans_to_sel(u32 val) 740 + { 741 + u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 }; 742 + int i; 743 + 744 + /* Select the smaller and equal supported value */ 745 + for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) { 746 + if (val < vinovp_tbl[i+1]) 747 + break; 748 + } 749 + return i; 750 + } 751 + 752 + static int mt6360_chg_init_setting(struct mt6360_chg_info *mci) 753 + { 754 + int ret; 755 + u32 sel; 756 + 757 + sel = mt6360_vinovp_trans_to_sel(mci->vinovp); 758 + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19, 759 + MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT); 760 + if (ret) 761 + return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__); 762 + ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE, 763 + MT6360_USBCHGEN_MASK, 0); 764 + if (ret) 765 + return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__); 766 + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2, 767 + MT6360_IINLMTSEL_MASK, 768 + MT6360_IINLMTSEL_AICR << 769 + MT6360_IINLMTSEL_SHFT); 770 + if (ret) 771 + return dev_err_probe(mci->dev, ret, 772 + "%s: Failed to switch iinlmtsel to aicr\n", __func__); 773 + usleep_range(5000, 6000); 774 + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3, 775 + MT6360_ILIM_EN_MASK, 0); 776 + if (ret) 777 + return dev_err_probe(mci->dev, ret, 778 + "%s: Failed to disable ilim\n", __func__); 779 + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10, 780 + MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK); 781 + if (ret) 782 + return dev_err_probe(mci->dev, ret, 783 + "%s: Failed to config otg oc to 3A\n", __func__); 784 + return 0; 785 + } 786 + 787 + static int mt6360_charger_probe(struct platform_device *pdev) 788 + { 789 + struct mt6360_chg_info *mci; 790 + struct power_supply_config charger_cfg = {}; 791 + struct regulator_config config = { }; 792 + int ret; 793 + 794 + mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL); 795 + if (!mci) 796 + return -ENOMEM; 797 + 798 + mci->dev = &pdev->dev; 799 + mci->vinovp = 6500000; 800 + mutex_init(&mci->chgdet_lock); 801 + platform_set_drvdata(pdev, mci); 802 + devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work); 803 + 804 + ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp); 805 + if (ret) 806 + dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n"); 807 + 808 + mci->regmap = dev_get_regmap(pdev->dev.parent, NULL); 809 + if (!mci->regmap) 810 + return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n"); 811 + 812 + ret = mt6360_chg_init_setting(mci); 813 + if (ret) 814 + return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n"); 815 + 816 + memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc)); 817 + mci->psy_desc.name = dev_name(&pdev->dev); 818 + charger_cfg.drv_data = mci; 819 + charger_cfg.of_node = pdev->dev.of_node; 820 + mci->psy = devm_power_supply_register(&pdev->dev, 821 + &mci->psy_desc, &charger_cfg); 822 + if (IS_ERR(mci->psy)) 823 + return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy), 824 + "Failed to register power supply dev\n"); 825 + 826 + 827 + ret = mt6360_chg_irq_register(pdev); 828 + if (ret) 829 + return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n"); 830 + 831 + config.dev = &pdev->dev; 832 + config.regmap = mci->regmap; 833 + mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc, 834 + &config); 835 + if (IS_ERR(mci->otg_rdev)) 836 + return PTR_ERR(mci->otg_rdev); 837 + 838 + schedule_work(&mci->chrdet_work); 839 + 840 + return 0; 841 + } 842 + 843 + static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = { 844 + { .compatible = "mediatek,mt6360-chg", }, 845 + {}, 846 + }; 847 + MODULE_DEVICE_TABLE(of, mt6360_charger_of_id); 848 + 849 + static const struct platform_device_id mt6360_charger_id[] = { 850 + { "mt6360-chg", 0 }, 851 + {}, 852 + }; 853 + MODULE_DEVICE_TABLE(platform, mt6360_charger_id); 854 + 855 + static struct platform_driver mt6360_charger_driver = { 856 + .driver = { 857 + .name = "mt6360-chg", 858 + .of_match_table = of_match_ptr(mt6360_charger_of_id), 859 + }, 860 + .probe = mt6360_charger_probe, 861 + .id_table = mt6360_charger_id, 862 + }; 863 + module_platform_driver(mt6360_charger_driver); 864 + 865 + MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>"); 866 + MODULE_DESCRIPTION("MT6360 Charger Driver"); 867 + MODULE_LICENSE("GPL");
+2
include/linux/linear_range.h
··· 41 41 int linear_range_get_selector_high(const struct linear_range *r, 42 42 unsigned int val, unsigned int *selector, 43 43 bool *found); 44 + void linear_range_get_selector_within(const struct linear_range *r, 45 + unsigned int val, unsigned int *selector); 44 46 int linear_range_get_selector_low_array(const struct linear_range *r, 45 47 int ranges, unsigned int val, 46 48 unsigned int *selector, bool *found);
+31
lib/linear_ranges.c
··· 241 241 } 242 242 EXPORT_SYMBOL_GPL(linear_range_get_selector_high); 243 243 244 + /** 245 + * linear_range_get_selector_within - return linear range selector for value 246 + * @r: pointer to linear range where selector is looked from 247 + * @val: value for which the selector is searched 248 + * @selector: address where found selector value is updated 249 + * 250 + * Return selector for which range value is closest match for given 251 + * input value. Value is matching if it is equal or lower than given 252 + * value. But return maximum selector if given value is higher than 253 + * maximum value. 254 + */ 255 + void linear_range_get_selector_within(const struct linear_range *r, 256 + unsigned int val, unsigned int *selector) 257 + { 258 + if (r->min > val) { 259 + *selector = r->min_sel; 260 + return; 261 + } 262 + 263 + if (linear_range_get_max_value(r) < val) { 264 + *selector = r->max_sel; 265 + return; 266 + } 267 + 268 + if (r->step == 0) 269 + *selector = r->min_sel; 270 + else 271 + *selector = (val - r->min) / r->step + r->min_sel; 272 + } 273 + EXPORT_SYMBOL_GPL(linear_range_get_selector_within); 274 + 244 275 MODULE_DESCRIPTION("linear-ranges helper"); 245 276 MODULE_LICENSE("GPL");