/* * The Total Accumulated Charge registers store the total accumulated * charge delivered from the VS source to a portable device. The total * value is calculated using four registers, from 01h to 04h. The bit * weighting of the registers is given in mA/hrs.
*/ #define UCS1002_REG_TOTAL_ACC_CHARGE 0x01
/* Other Status Register */ #define UCS1002_REG_OTHER_STATUS 0x0f # define F_ADET_PIN BIT(4) # define F_CHG_ACT BIT(3)
staticint ucs1002_get_current(struct ucs1002_info *info, union power_supply_propval *val)
{ /* * The Current Measurement register stores the measured * current value delivered to the portable device. The range * is from 9.76 mA to 2.5 A.
*/ staticconstint bit_weights_uA[BITS_PER_TYPE(u8)] = {
9760, 19500, 39000, 78100, 156200, 312300, 624600, 1249300,
}; unsignedlong current_measurement; unsignedint reg; int i, ret;
ret = regmap_read(info->regmap, UCS1002_REG_CURRENT_MEASUREMENT, ®); if (ret) return ret;
/* * The Current Limit register stores the maximum current used by the * port switch. The range is from 500mA to 2.5 A.
*/ staticconst u32 ucs1002_current_limit_uA[] = {
500000, 900000, 1000000, 1200000, 1500000, 1800000, 2000000, 2500000,
};
staticint ucs1002_get_max_current(struct ucs1002_info *info, union power_supply_propval *val)
{ unsignedint reg; int ret;
if (info->output_disable) {
val->intval = 0; return 0;
}
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®); if (ret) return ret;
for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) { if (val == ucs1002_current_limit_uA[idx]) break;
}
if (idx == ARRAY_SIZE(ucs1002_current_limit_uA)) return -EINVAL;
ret = regmap_write(info->regmap, UCS1002_REG_ILIMIT, idx); if (ret) return ret; /* * Any current limit setting exceeding the one set via ILIM * pin will be rejected, so we read out freshly changed limit * to make sure that it took effect.
*/
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®); if (ret) return ret;
if (reg != idx) return -EINVAL;
info->output_disable = false;
if (info->rdev && info->rdev->use_count &&
!regulator_is_enabled_regmap(info->rdev))
regulator_enable_regmap(info->rdev);
return 0;
}
staticint ucs1002_set_usb_type(struct ucs1002_info *info, int val)
{ unsignedint mode;
switch (val) { /* * POWER_SUPPLY_USB_TYPE_UNKNOWN == 0, map this to dedicated for * userspace API compatibility with older versions of this driver * which mapped 0 to dedicated.
*/ case POWER_SUPPLY_USB_TYPE_UNKNOWN: case POWER_SUPPLY_USB_TYPE_PD:
mode = V_SET_ACTIVE_MODE_DEDICATED; break; case POWER_SUPPLY_USB_TYPE_SDP:
mode = V_SET_ACTIVE_MODE_BC12_SDP; break; case POWER_SUPPLY_USB_TYPE_DCP:
mode = V_SET_ACTIVE_MODE_BC12_DCP; break; case POWER_SUPPLY_USB_TYPE_CDP:
mode = V_SET_ACTIVE_MODE_BC12_CDP; break; default: return -EINVAL;
}
staticint ucs1002_get_usb_type(struct ucs1002_info *info, union power_supply_propval *val)
{ enum power_supply_usb_type type; unsignedint reg; int ret;
ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, ®); if (ret) return ret;
switch (reg & F_ACTIVE_MODE_MASK) { default:
type = POWER_SUPPLY_USB_TYPE_UNKNOWN; break; case F_ACTIVE_MODE_DEDICATED:
type = POWER_SUPPLY_USB_TYPE_PD; break; case F_ACTIVE_MODE_BC12_SDP:
type = POWER_SUPPLY_USB_TYPE_SDP; break; case F_ACTIVE_MODE_BC12_DCP:
type = POWER_SUPPLY_USB_TYPE_DCP; break; case F_ACTIVE_MODE_BC12_CDP:
type = POWER_SUPPLY_USB_TYPE_CDP; break;
}
ret = regmap_read(info->regmap, UCS1002_REG_INTERRUPT_STATUS, ®); if (ret) return;
/* bad health and no status change, just schedule us again in a while */ if ((reg & F_ERR) && info->health != POWER_SUPPLY_HEALTH_GOOD) {
schedule_delayed_work(&info->health_poll,
msecs_to_jiffies(2000)); return;
}
/* * If the output is disabled due to 0 maximum current, just pretend the * enable did work. The regulator will be enabled as soon as we get a * a non-zero maximum current budget.
*/ if (info->output_disable) return 0;
ret = regmap_read(info->regmap, UCS1002_REG_PRODUCT_ID, ®val); if (ret) {
dev_err(dev, "Failed to read product ID: %d\n", ret); return ret;
}
if (regval != UCS1002_PRODUCT_ID) {
dev_err(dev, "Product ID does not match (0x%02x != 0x%02x)\n",
regval, UCS1002_PRODUCT_ID); return -ENODEV;
}
/* Enable charge rationing by default */
ret = regmap_update_bits(info->regmap, UCS1002_REG_GENERAL_CFG,
F_RATION_EN, F_RATION_EN); if (ret) {
dev_err(dev, "Failed to read general config: %d\n", ret); return ret;
}
/* * Ignore the M1, M2, PWR_EN, and EM_EN pin states. Set active * mode selection to BC1.2 CDP.
*/
ret = regmap_update_bits(info->regmap, UCS1002_REG_SWITCH_CFG,
V_SET_ACTIVE_MODE_MASK | F_PIN_IGNORE,
V_SET_ACTIVE_MODE_BC12_CDP | F_PIN_IGNORE); if (ret) {
dev_err(dev, "Failed to configure default mode: %d\n", ret); return ret;
} /* * Be safe and set initial current limit to 500mA
*/
ret = ucs1002_set_max_current(info, 500000); if (ret) {
dev_err(dev, "Failed to set max current default: %d\n", ret); return ret;
}
info->charger = devm_power_supply_register(dev, &ucs1002_charger_desc,
&charger_config);
ret = PTR_ERR_OR_ZERO(info->charger); if (ret) {
dev_err(dev, "Failed to register power supply: %d\n", ret); return ret;
}
ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, ®val); if (ret) {
dev_err(dev, "Failed to read pin status: %d\n", ret); return ret;
}
info->regulator_descriptor =
devm_kmemdup(dev, &ucs1002_regulator_descriptor, sizeof(ucs1002_regulator_descriptor),
GFP_KERNEL); if (!info->regulator_descriptor) return -ENOMEM;
MODULE_DESCRIPTION("Microchip UCS1002 Programmable USB Port Power Controller");
MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.