summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/charge_manager.c80
1 files changed, 58 insertions, 22 deletions
diff --git a/common/charge_manager.c b/common/charge_manager.c
index c7620a69dc..46fcb91730 100644
--- a/common/charge_manager.c
+++ b/common/charge_manager.c
@@ -86,7 +86,13 @@ static timestamp_t delayed_override_deadline;
static volatile uint32_t source_port_bitmap;
BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_COUNT);
#endif
-static uint8_t source_port_last_rp[CONFIG_USB_PD_PORT_COUNT];
+static uint8_t source_port_rp[CONFIG_USB_PD_PORT_COUNT];
+
+#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT
+/* 3A on one port and 1.5A on the rest */
+BUILD_ASSERT(CONFIG_USB_PD_PORT_COUNT * 1500 + 1500 <=
+ CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT);
+#endif
/*
* charge_manager initially operates in safe mode until asked to leave (through
@@ -178,7 +184,7 @@ static void charge_manager_init(void)
if (!is_pd_port(i))
dualrole_capability[i] = CAP_DEDICATED;
if (is_pd_port(i))
- source_port_last_rp[i] = CONFIG_USB_PD_PULLUP;
+ source_port_rp[i] = CONFIG_USB_PD_PULLUP;
}
}
DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_CHARGE_MANAGER_INIT);
@@ -222,7 +228,7 @@ static int charge_manager_get_source_current(int port)
if (!is_pd_port(port))
return 0;
- switch (source_port_last_rp[port]) {
+ switch (source_port_rp[port]) {
case TYPEC_RP_3A0:
return 3000;
case TYPEC_RP_1A5:
@@ -1041,10 +1047,49 @@ int charge_manager_get_power_limit_uw(void)
}
#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
+
+static inline int has_other_active_source(int port)
+{
+ return source_port_bitmap & ~(1 << port);
+}
+
+static inline int is_active_source(int port)
+{
+ return source_port_bitmap & (1 << port);
+}
+
+static int can_supply_max_current(int port)
+{
+#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT
+ /*
+ * This guarantees active 3A source continues to supply 3A.
+ *
+ * Since redistribution occurs sequentially, younger ports get
+ * priority. Priority surfaces only when 3A source is released.
+ * That is, when 3A source is released, the youngest active
+ * port gets 3A.
+ */
+ int p;
+ if (!is_active_source(port) && has_other_active_source(port))
+ /* Another port will get 3A */
+ return 0;
+ for (p = 0; p < CONFIG_USB_PD_PORT_COUNT; p++) {
+ if (p == port)
+ continue;
+ if (is_active_source(p) && source_port_rp[p] ==
+ CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT)
+ return 0;
+ }
+ return 1;
+#else
+ return !has_other_active_source(port);
+#endif /* CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT */
+}
+
void charge_manager_source_port(int port, int enable)
{
uint32_t prev_bitmap = source_port_bitmap;
- int p;
+ int p, rp;
if (enable)
atomic_or(&source_port_bitmap, 1 << port);
@@ -1057,14 +1102,10 @@ void charge_manager_source_port(int port, int enable)
/* Set port limit according to policy */
for (p = 0; p < CONFIG_USB_PD_PORT_COUNT; p++) {
- /*
- * if we are the only active source port or there is none,
- * advertise all the available power.
- */
- int rp = (source_port_bitmap & ~(1 << p)) ? CONFIG_USB_PD_PULLUP
- : CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT;
-
- source_port_last_rp[p] = rp;
+ rp = can_supply_max_current(p) ?
+ CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT :
+ CONFIG_USB_PD_PULLUP;
+ source_port_rp[p] = rp;
#ifdef CONFIG_USB_PD_LOGGING
if (is_connected(p) && !is_sink(p))
@@ -1079,18 +1120,13 @@ void charge_manager_source_port(int port, int enable)
int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port)
{
- /* Are there any other connected sinks? */
- if (source_port_bitmap & ~(1 << port)) {
- *src_pdo = pd_src_pdo;
- return pd_src_pdo_cnt;
+ if (can_supply_max_current(port)) {
+ *src_pdo = pd_src_pdo_max;
+ return pd_src_pdo_max_cnt;
}
- /*
- * If not, send the maximum current since we're sourcing on only one
- * port.
- */
- *src_pdo = pd_src_pdo_max;
- return pd_src_pdo_max_cnt;
+ *src_pdo = pd_src_pdo;
+ return pd_src_pdo_cnt;
}
#endif /* CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT */