Revert "typec: tcpm: Only request matching pdos"

Commit 57e6f0d7b8 ("typec: tcpm: Only request matching pdos") is causing
a regression, before this commit e.g. the GPD win and GPD pocket devices
were charging at 9V 3A with a PD charger, now they are instead slowly
discharging  at 5V 0.4A, as this commit causes the ports max_snk_mv/ma/mw
settings to be completely ignored.

Arguably the way to fix this would be to add a PDO_VAR() describing the
voltage range to the snk_caps of boards which can handle any voltage in
their range, but the "typec: tcpm: Only request matching pdos" commit
looks at the type of PDO advertised by the source/charger and if that
is fixed (as it typically is) only compairs against PDO_FIXED entries
in the snk_caps so supporting a range of voltage would require adding a
PDO_FIXED entry for *every possible* voltage to snk_caps.

AFAICT there is no reason why a fixed source_cap cannot be matched against
a variable snk_cap, so at a minimum the commit should be rewritten to
support that.

For now lets revert the "typec: tcpm: Only request matching pdos" commit,
fixing the regression.

Fixes: 57e6f0d7b8 ("typec: tcpm: Only request matching pdos")
Cc: Badhri Jagan Sridharan <badhri@google.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Acked-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Hans de Goede 2018-03-06 10:50:05 +01:00 committed by Greg Kroah-Hartman
parent df6b074dbe
commit 6f566af346
1 changed files with 42 additions and 121 deletions

View File

@ -252,9 +252,6 @@ struct tcpm_port {
unsigned int nr_src_pdo;
u32 snk_pdo[PDO_MAX_OBJECTS];
unsigned int nr_snk_pdo;
unsigned int nr_fixed; /* number of fixed sink PDOs */
unsigned int nr_var; /* number of variable sink PDOs */
unsigned int nr_batt; /* number of battery sink PDOs */
u32 snk_vdo[VDO_MAX_OBJECTS];
unsigned int nr_snk_vdo;
@ -1770,90 +1767,39 @@ static int tcpm_pd_check_request(struct tcpm_port *port)
return 0;
}
#define min_power(x, y) min(pdo_max_power(x), pdo_max_power(y))
#define min_current(x, y) min(pdo_max_current(x), pdo_max_current(y))
static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
int *src_pdo)
static int tcpm_pd_select_pdo(struct tcpm_port *port)
{
unsigned int i, j, max_mw = 0, max_mv = 0, mw = 0, mv = 0, ma = 0;
unsigned int i, max_mw = 0, max_mv = 0;
int ret = -EINVAL;
/*
* Select the source PDO providing the most power which has a
* matchig sink cap.
* Select the source PDO providing the most power while staying within
* the board's voltage limits. Prefer PDO providing exp
*/
for (i = 0; i < port->nr_source_caps; i++) {
u32 pdo = port->source_caps[i];
enum pd_pdo_type type = pdo_type(pdo);
unsigned int mv, ma, mw;
if (type == PDO_TYPE_FIXED) {
for (j = 0; j < port->nr_fixed; j++) {
if (pdo_fixed_voltage(pdo) ==
pdo_fixed_voltage(port->snk_pdo[j])) {
ma = min_current(pdo, port->snk_pdo[j]);
mv = pdo_fixed_voltage(pdo);
mw = ma * mv / 1000;
if (mw > max_mw ||
(mw == max_mw && mv > max_mv)) {
ret = 0;
*src_pdo = i;
*sink_pdo = j;
max_mw = mw;
max_mv = mv;
}
/* There could only be one fixed pdo
* at a specific voltage level.
* So breaking here.
*/
break;
}
}
} else if (type == PDO_TYPE_BATT) {
for (j = port->nr_fixed;
j < port->nr_fixed +
port->nr_batt;
j++) {
if (pdo_min_voltage(pdo) >=
pdo_min_voltage(port->snk_pdo[j]) &&
pdo_max_voltage(pdo) <=
pdo_max_voltage(port->snk_pdo[j])) {
mw = min_power(pdo, port->snk_pdo[j]);
mv = pdo_min_voltage(pdo);
if (mw > max_mw ||
(mw == max_mw && mv > max_mv)) {
ret = 0;
*src_pdo = i;
*sink_pdo = j;
max_mw = mw;
max_mv = mv;
}
}
}
} else if (type == PDO_TYPE_VAR) {
for (j = port->nr_fixed +
port->nr_batt;
j < port->nr_fixed +
port->nr_batt +
port->nr_var;
j++) {
if (pdo_min_voltage(pdo) >=
pdo_min_voltage(port->snk_pdo[j]) &&
pdo_max_voltage(pdo) <=
pdo_max_voltage(port->snk_pdo[j])) {
ma = min_current(pdo, port->snk_pdo[j]);
mv = pdo_min_voltage(pdo);
mw = ma * mv / 1000;
if (mw > max_mw ||
(mw == max_mw && mv > max_mv)) {
ret = 0;
*src_pdo = i;
*sink_pdo = j;
max_mw = mw;
max_mv = mv;
}
}
}
if (type == PDO_TYPE_FIXED)
mv = pdo_fixed_voltage(pdo);
else
mv = pdo_min_voltage(pdo);
if (type == PDO_TYPE_BATT) {
mw = pdo_max_power(pdo);
} else {
ma = min(pdo_max_current(pdo),
port->max_snk_ma);
mw = ma * mv / 1000;
}
/* Perfer higher voltages if available */
if ((mw > max_mw || (mw == max_mw && mv > max_mv)) &&
mv <= port->max_snk_mv) {
ret = i;
max_mw = mw;
max_mv = mv;
}
}
@ -1865,14 +1811,13 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
unsigned int mv, ma, mw, flags;
unsigned int max_ma, max_mw;
enum pd_pdo_type type;
int src_pdo_index, snk_pdo_index;
u32 pdo, matching_snk_pdo;
int index;
u32 pdo;
if (tcpm_pd_select_pdo(port, &snk_pdo_index, &src_pdo_index) < 0)
index = tcpm_pd_select_pdo(port);
if (index < 0)
return -EINVAL;
pdo = port->source_caps[src_pdo_index];
matching_snk_pdo = port->snk_pdo[snk_pdo_index];
pdo = port->source_caps[index];
type = pdo_type(pdo);
if (type == PDO_TYPE_FIXED)
@ -1880,28 +1825,26 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
else
mv = pdo_min_voltage(pdo);
/* Select maximum available current within the sink pdo's limit */
/* Select maximum available current within the board's power limit */
if (type == PDO_TYPE_BATT) {
mw = min_power(pdo, matching_snk_pdo);
ma = 1000 * mw / mv;
mw = pdo_max_power(pdo);
ma = 1000 * min(mw, port->max_snk_mw) / mv;
} else {
ma = min_current(pdo, matching_snk_pdo);
mw = ma * mv / 1000;
ma = min(pdo_max_current(pdo),
1000 * port->max_snk_mw / mv);
}
ma = min(ma, port->max_snk_ma);
flags = RDO_USB_COMM | RDO_NO_SUSPEND;
/* Set mismatch bit if offered power is less than operating power */
mw = ma * mv / 1000;
max_ma = ma;
max_mw = mw;
if (mw < port->operating_snk_mw) {
flags |= RDO_CAP_MISMATCH;
if (type == PDO_TYPE_BATT &&
(pdo_max_power(matching_snk_pdo) > pdo_max_power(pdo)))
max_mw = pdo_max_power(matching_snk_pdo);
else if (pdo_max_current(matching_snk_pdo) >
pdo_max_current(pdo))
max_ma = pdo_max_current(matching_snk_pdo);
max_mw = port->operating_snk_mw;
max_ma = max_mw * 1000 / mv;
}
tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d",
@ -1910,16 +1853,16 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
port->polarity);
if (type == PDO_TYPE_BATT) {
*rdo = RDO_BATT(src_pdo_index + 1, mw, max_mw, flags);
*rdo = RDO_BATT(index + 1, mw, max_mw, flags);
tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s",
src_pdo_index, mv, mw,
index, mv, mw,
flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
} else {
*rdo = RDO_FIXED(src_pdo_index + 1, ma, max_ma, flags);
*rdo = RDO_FIXED(index + 1, ma, max_ma, flags);
tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s",
src_pdo_index, mv, ma,
index, mv, ma,
flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
}
@ -3650,19 +3593,6 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
}
EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities);
static int nr_type_pdos(const u32 *pdo, unsigned int nr_pdo,
enum pd_pdo_type type)
{
int count = 0;
int i;
for (i = 0; i < nr_pdo; i++) {
if (pdo_type(pdo[i]) == type)
count++;
}
return count;
}
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
{
struct tcpm_port *port;
@ -3708,15 +3638,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
tcpc->config->nr_src_pdo);
port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo,
tcpc->config->nr_snk_pdo);
port->nr_fixed = nr_type_pdos(port->snk_pdo,
port->nr_snk_pdo,
PDO_TYPE_FIXED);
port->nr_var = nr_type_pdos(port->snk_pdo,
port->nr_snk_pdo,
PDO_TYPE_VAR);
port->nr_batt = nr_type_pdos(port->snk_pdo,
port->nr_snk_pdo,
PDO_TYPE_BATT);
port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo,
tcpc->config->nr_snk_vdo);