Skip to content

Commit b04e174

Browse files
saranyagopal1gregkh
authored andcommitted
usb: typec: ucsi: Register USB Power Delivery Capabilities
UCSI allows the USB PD capabilities to be read with the GET_PDO command. This will register those capabilities and make them visible to user space. Reviewed-by: Heikki Krogerus <[email protected]> Co-developed-by: Rajaram Regupathy <[email protected]> Signed-off-by: Rajaram Regupathy <[email protected]> Signed-off-by: Saranya Gopal <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 9aa1afc commit b04e174

File tree

2 files changed

+158
-13
lines changed

2 files changed

+158
-13
lines changed

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 150 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,9 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
562562
}
563563
}
564564

565-
static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
566-
u32 *pdos, int offset, int num_pdos)
565+
static int ucsi_read_pdos(struct ucsi_connector *con,
566+
enum typec_role role, int is_partner,
567+
u32 *pdos, int offset, int num_pdos)
567568
{
568569
struct ucsi *ucsi = con->ucsi;
569570
u64 command;
@@ -573,7 +574,7 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
573574
command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
574575
command |= UCSI_GET_PDOS_PDO_OFFSET(offset);
575576
command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1);
576-
command |= UCSI_GET_PDOS_SRC_PDOS;
577+
command |= is_source(role) ? UCSI_GET_PDOS_SRC_PDOS : 0;
577578
ret = ucsi_send_command(ucsi, command, pdos + offset,
578579
num_pdos * sizeof(u32));
579580
if (ret < 0 && ret != -ETIMEDOUT)
@@ -582,30 +583,43 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
582583
return ret;
583584
}
584585

585-
static int ucsi_get_src_pdos(struct ucsi_connector *con)
586+
static int ucsi_get_pdos(struct ucsi_connector *con, enum typec_role role,
587+
int is_partner, u32 *pdos)
586588
{
589+
u8 num_pdos;
587590
int ret;
588591

589592
/* UCSI max payload means only getting at most 4 PDOs at a time */
590-
ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS);
593+
ret = ucsi_read_pdos(con, role, is_partner, pdos, 0, UCSI_MAX_PDOS);
591594
if (ret < 0)
592595
return ret;
593596

594-
con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
595-
if (con->num_pdos < UCSI_MAX_PDOS)
596-
return 0;
597+
num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
598+
if (num_pdos < UCSI_MAX_PDOS)
599+
return num_pdos;
597600

598601
/* get the remaining PDOs, if any */
599-
ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS,
600-
PDO_MAX_OBJECTS - UCSI_MAX_PDOS);
602+
ret = ucsi_read_pdos(con, role, is_partner, pdos, UCSI_MAX_PDOS,
603+
PDO_MAX_OBJECTS - UCSI_MAX_PDOS);
601604
if (ret < 0)
602605
return ret;
603606

604-
con->num_pdos += ret / sizeof(u32);
607+
return ret / sizeof(u32) + num_pdos;
608+
}
609+
610+
static int ucsi_get_src_pdos(struct ucsi_connector *con)
611+
{
612+
int ret;
613+
614+
ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, con->src_pdos);
615+
if (ret < 0)
616+
return ret;
617+
618+
con->num_pdos = ret;
605619

606620
ucsi_port_psy_changed(con);
607621

608-
return 0;
622+
return ret;
609623
}
610624

611625
static int ucsi_check_altmodes(struct ucsi_connector *con)
@@ -630,6 +644,72 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
630644
return ret;
631645
}
632646

647+
static int ucsi_register_partner_pdos(struct ucsi_connector *con)
648+
{
649+
struct usb_power_delivery_desc desc = { con->ucsi->cap.pd_version };
650+
struct usb_power_delivery_capabilities_desc caps;
651+
struct usb_power_delivery_capabilities *cap;
652+
int ret;
653+
654+
if (con->partner_pd)
655+
return 0;
656+
657+
con->partner_pd = usb_power_delivery_register(NULL, &desc);
658+
if (IS_ERR(con->partner_pd))
659+
return PTR_ERR(con->partner_pd);
660+
661+
ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, caps.pdo);
662+
if (ret > 0) {
663+
if (ret < PDO_MAX_OBJECTS)
664+
caps.pdo[ret] = 0;
665+
666+
caps.role = TYPEC_SOURCE;
667+
cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps);
668+
if (IS_ERR(cap))
669+
return PTR_ERR(cap);
670+
671+
con->partner_source_caps = cap;
672+
673+
ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd);
674+
if (ret) {
675+
usb_power_delivery_unregister_capabilities(con->partner_source_caps);
676+
return ret;
677+
}
678+
}
679+
680+
ret = ucsi_get_pdos(con, TYPEC_SINK, 1, caps.pdo);
681+
if (ret > 0) {
682+
if (ret < PDO_MAX_OBJECTS)
683+
caps.pdo[ret] = 0;
684+
685+
caps.role = TYPEC_SINK;
686+
687+
cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps);
688+
if (IS_ERR(cap))
689+
return PTR_ERR(cap);
690+
691+
con->partner_sink_caps = cap;
692+
693+
ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd);
694+
if (ret) {
695+
usb_power_delivery_unregister_capabilities(con->partner_sink_caps);
696+
return ret;
697+
}
698+
}
699+
700+
return 0;
701+
}
702+
703+
static void ucsi_unregister_partner_pdos(struct ucsi_connector *con)
704+
{
705+
usb_power_delivery_unregister_capabilities(con->partner_sink_caps);
706+
con->partner_sink_caps = NULL;
707+
usb_power_delivery_unregister_capabilities(con->partner_source_caps);
708+
con->partner_source_caps = NULL;
709+
usb_power_delivery_unregister(con->partner_pd);
710+
con->partner_pd = NULL;
711+
}
712+
633713
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
634714
{
635715
switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
@@ -638,6 +718,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
638718
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
639719
ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0);
640720
ucsi_partner_task(con, ucsi_check_altmodes, 30, 0);
721+
ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ);
641722
break;
642723
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
643724
con->rdo = 0;
@@ -696,6 +777,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
696777
if (!con->partner)
697778
return;
698779

780+
ucsi_unregister_partner_pdos(con);
699781
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
700782
typec_unregister_partner(con->partner);
701783
con->partner = NULL;
@@ -800,6 +882,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
800882
if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
801883
ucsi_register_partner(con);
802884
ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
885+
886+
if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
887+
UCSI_CONSTAT_PWR_OPMODE_PD)
888+
ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ);
803889
} else {
804890
ucsi_unregister_partner(con);
805891
}
@@ -1036,6 +1122,9 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
10361122

10371123
static int ucsi_register_port(struct ucsi *ucsi, int index)
10381124
{
1125+
struct usb_power_delivery_desc desc = { ucsi->cap.pd_version};
1126+
struct usb_power_delivery_capabilities_desc pd_caps;
1127+
struct usb_power_delivery_capabilities *pd_cap;
10391128
struct ucsi_connector *con = &ucsi->connector[index];
10401129
struct typec_capability *cap = &con->typec_cap;
10411130
enum typec_accessory *accessory = cap->accessory;
@@ -1114,6 +1203,41 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
11141203
goto out;
11151204
}
11161205

1206+
con->pd = usb_power_delivery_register(ucsi->dev, &desc);
1207+
1208+
ret = ucsi_get_pdos(con, TYPEC_SOURCE, 0, pd_caps.pdo);
1209+
if (ret > 0) {
1210+
if (ret < PDO_MAX_OBJECTS)
1211+
pd_caps.pdo[ret] = 0;
1212+
1213+
pd_caps.role = TYPEC_SOURCE;
1214+
pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps);
1215+
if (IS_ERR(pd_cap)) {
1216+
ret = PTR_ERR(pd_cap);
1217+
goto out;
1218+
}
1219+
1220+
con->port_source_caps = pd_cap;
1221+
typec_port_set_usb_power_delivery(con->port, con->pd);
1222+
}
1223+
1224+
memset(&pd_caps, 0, sizeof(pd_caps));
1225+
ret = ucsi_get_pdos(con, TYPEC_SINK, 0, pd_caps.pdo);
1226+
if (ret > 0) {
1227+
if (ret < PDO_MAX_OBJECTS)
1228+
pd_caps.pdo[ret] = 0;
1229+
1230+
pd_caps.role = TYPEC_SINK;
1231+
pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps);
1232+
if (IS_ERR(pd_cap)) {
1233+
ret = PTR_ERR(pd_cap);
1234+
goto out;
1235+
}
1236+
1237+
con->port_sink_caps = pd_cap;
1238+
typec_port_set_usb_power_delivery(con->port, con->pd);
1239+
}
1240+
11171241
/* Alternate modes */
11181242
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
11191243
if (ret) {
@@ -1152,8 +1276,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
11521276
if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
11531277
typec_set_pwr_role(con->port,
11541278
!!(con->status.flags & UCSI_CONSTAT_PWR_DIR));
1155-
ucsi_pwr_opmode_change(con);
11561279
ucsi_register_partner(con);
1280+
ucsi_pwr_opmode_change(con);
11571281
ucsi_port_psy_changed(con);
11581282
}
11591283

@@ -1259,6 +1383,13 @@ static int ucsi_init(struct ucsi *ucsi)
12591383
ucsi_unregister_port_psy(con);
12601384
if (con->wq)
12611385
destroy_workqueue(con->wq);
1386+
1387+
usb_power_delivery_unregister_capabilities(con->port_sink_caps);
1388+
con->port_sink_caps = NULL;
1389+
usb_power_delivery_unregister_capabilities(con->port_source_caps);
1390+
con->port_source_caps = NULL;
1391+
usb_power_delivery_unregister(con->pd);
1392+
con->pd = NULL;
12621393
typec_unregister_port(con->port);
12631394
con->port = NULL;
12641395
}
@@ -1422,6 +1553,12 @@ void ucsi_unregister(struct ucsi *ucsi)
14221553
ucsi_unregister_port_psy(&ucsi->connector[i]);
14231554
if (ucsi->connector[i].wq)
14241555
destroy_workqueue(ucsi->connector[i].wq);
1556+
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps);
1557+
ucsi->connector[i].port_sink_caps = NULL;
1558+
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps);
1559+
ucsi->connector[i].port_source_caps = NULL;
1560+
usb_power_delivery_unregister(ucsi->connector[i].pd);
1561+
ucsi->connector[i].pd = NULL;
14251562
typec_unregister_port(ucsi->connector[i].port);
14261563
}
14271564

drivers/usb/typec/ucsi/ucsi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ struct ucsi_connector {
339339
u32 src_pdos[PDO_MAX_OBJECTS];
340340
int num_pdos;
341341

342+
/* USB PD objects */
343+
struct usb_power_delivery *pd;
344+
struct usb_power_delivery_capabilities *port_source_caps;
345+
struct usb_power_delivery_capabilities *port_sink_caps;
346+
struct usb_power_delivery *partner_pd;
347+
struct usb_power_delivery_capabilities *partner_source_caps;
348+
struct usb_power_delivery_capabilities *partner_sink_caps;
349+
342350
struct usb_role_switch *usb_role_sw;
343351
};
344352

0 commit comments

Comments
 (0)