From c4ffb5aa4cfd4d910af1b718874536bd64de38d9 Mon Sep 17 00:00:00 2001 From: William Wu Date: Thu, 22 Dec 2022 14:59:33 +0800 Subject: [PATCH] mfd: fusb302: Fix no response to get sink cap from source The fusb302 use the legacy driver on RK3399 kernel-4.4/4.19 platforms. The legacy driver doesn't support to send sink capabilities message, it cause RK3399 Type-C USB ADB fail to connected to MacBook with a Type-C to Type-C cable. This patch adds the sink capabilities message and migrate workqueue to RT priority for processing pd messages. 1. Add sink capabilities message for fixed supply and battery supply. 2. Migrate workqueue to RT priority refer to the commit 3ed8e1c2ac9914a2fcb08ec13476b85319536cea upstream. 3. Move the interrupt handling from a threaded interrupt handler to a non-threaded interrupt handler which normally queues the new interrupt handling work directly. Signed-off-by: William Wu Change-Id: I13df67d71d1f0c11f4256daacd2243dcfb8ae414 --- drivers/mfd/fusb302.c | 114 +++++++++++++++++++++++++++++++----------- drivers/mfd/fusb302.h | 17 +++++-- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/drivers/mfd/fusb302.c b/drivers/mfd/fusb302.c index 3c1190f3f6a7..1044b5958c01 100644 --- a/drivers/mfd/fusb302.c +++ b/drivers/mfd/fusb302.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "fusb302.h" @@ -929,6 +931,7 @@ static void set_state_unattached(struct fusb30x_chip *chip) static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT) { int i; + uint32_t rec_load; struct PD_CAP_INFO *pd_cap_info = &chip->pd_cap_info; chip->send_head = ((chip->msg_id & 0x7) << 9) | @@ -962,24 +965,62 @@ static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT) (0 << 25) | (0 << 24); - switch (CAP_POWER_TYPE(chip->rec_load[chip->pos_power - 1])) { + rec_load = chip->rec_load[chip->pos_power - 1]; + switch (CAP_POWER_TYPE(rec_load)) { case 0: /* Fixed Supply */ - chip->send_load[0] |= CAP_FPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10; - chip->send_load[0] |= CAP_FPDO_CURRENT(chip->rec_load[chip->pos_power - 1]); + chip->sink_supply_type = 0; + chip->sink_volt = CAP_FPDO_VOLTAGE(rec_load); + chip->sink_opr_cur = CAP_FPDO_CURRENT(rec_load); + chip->send_load[0] |= chip->sink_volt << 10; + chip->send_load[0] |= chip->sink_opr_cur; break; case 1: - /* Battery */ - chip->send_load[0] |= CAP_VPDO_MAX_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 20; - chip->send_load[0] |= CAP_VPDO_MIN_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10; - chip->send_load[0] |= CAP_VPDO_CURRENT(chip->rec_load[chip->pos_power - 1]); + /* Battery Supply */ + chip->sink_supply_type = 1; + chip->sink_max_volt = + CAP_VPDO_MAX_VOLTAGE(rec_load); + chip->sink_min_volt = + CAP_VPDO_MIN_VOLTAGE(rec_load); + chip->sink_opr_power = + CAP_VPDO_CURRENT(rec_load); + chip->send_load[0] |= chip->sink_max_volt << 20; + chip->send_load[0] |= chip->sink_min_volt << 10; + chip->send_load[0] |= chip->sink_opr_power; break; default: - /* not meet battery caps */ + dev_warn(chip->dev, "No support supply req type %d\n", + CAP_POWER_TYPE(rec_load)); break; } break; case DMT_SINKCAPABILITIES: + chip->send_head |= (1 << 12) | (cmd & 0xf); + switch (chip->sink_supply_type) { + case 0: + /* + * Fixed Supply + * bit26 for 'USB Communiications Capable' + */ + chip->send_load[0] = + (chip->sink_supply_type << 30) | + (1 << 26) | + (chip->sink_volt << 10) | + (chip->sink_opr_cur); + break; + case 1: + /* Battery Supply */ + chip->send_load[0] = + (chip->sink_supply_type << 30) | + (chip->sink_max_volt << 20) | + (chip->sink_min_volt << 10) | + (chip->sink_opr_cur); + break; + default: + dev_warn(chip->dev, "No support sink supply type %d\n", + chip->sink_supply_type); + break; + } break; case DMT_VENDERDEFINED: break; @@ -2621,6 +2662,14 @@ static void fusb_state_snk_transition_sink(struct fusb30x_chip *chip, u32 evt) chip->notify.is_pd_connected = true; dev_info(chip->dev, "PD connected as UFP, fetching 5V\n"); + tcpm_get_message(chip); + if (PACKET_IS_CONTROL_MSG(chip->rec_head, + CMT_GETSINKCAP)) { + set_mesg(chip, DMT_SINKCAPABILITIES, + DATAMESSAGE); + chip->tx_state = tx_idle; + policy_send_data(chip); + } set_state(chip, policy_snk_ready); } else if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_SOURCECAPABILITIES)) { @@ -2672,7 +2721,12 @@ static void fusb_state_snk_transition_default(struct fusb30x_chip *chip, static void fusb_state_snk_ready(struct fusb30x_chip *chip, u32 evt) { if (evt & EVENT_RX) { - if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) { + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_GETSINKCAP)) { + set_mesg(chip, DMT_SINKCAPABILITIES, DATAMESSAGE); + chip->tx_state = tx_idle; + policy_send_data(chip); + } else if (PACKET_IS_DATA_MSG(chip->rec_head, + DMT_VENDERDEFINED)) { process_vdm_msg(chip); chip->work_continue |= EVENT_WORK_CONTINUE; chip->timer_state = T_DISABLED; @@ -3190,22 +3244,22 @@ static void state_machine_typec(struct fusb30x_chip *chip) BACK: if (chip->work_continue) { - queue_work(chip->fusb30x_wq, &chip->work); + kthread_queue_work(chip->irq_worker, &chip->irq_work); return; } if (!platform_get_device_irq_state(chip)) fusb_irq_enable(chip); else - queue_work(chip->fusb30x_wq, &chip->work); + kthread_queue_work(chip->irq_worker, &chip->irq_work); } static irqreturn_t cc_interrupt_handler(int irq, void *dev_id) { struct fusb30x_chip *chip = dev_id; - queue_work(chip->fusb30x_wq, &chip->work); fusb_irq_disable(chip); + kthread_queue_work(chip->irq_worker, &chip->irq_work); return IRQ_HANDLED; } @@ -3267,8 +3321,8 @@ static enum hrtimer_restart fusb_timer_handler(struct hrtimer *timer) } if (i != fusb30x_port_used) - queue_work(fusb30x_port_info[i]->fusb30x_wq, - &fusb30x_port_info[i]->work); + kthread_queue_work(fusb30x_port_info[i]->irq_worker, + &fusb30x_port_info[i]->irq_work); return HRTIMER_NORESTART; } @@ -3287,11 +3341,11 @@ static void fusb_initialize_timer(struct fusb30x_chip *chip) chip->timer_mux = T_DISABLED; } -static void fusb302_work_func(struct work_struct *work) +static void fusb302_work_func(struct kthread_work *work) { struct fusb30x_chip *chip; - chip = container_of(work, struct fusb30x_chip, work); + chip = container_of(work, struct fusb30x_chip, irq_work); if (!chip->suspended) state_machine_typec(chip); } @@ -3301,6 +3355,7 @@ static int fusb30x_probe(struct i2c_client *client, { struct fusb30x_chip *chip; struct PD_CAP_INFO *pd_cap_info; + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; int ret; char *string[2]; @@ -3327,8 +3382,11 @@ static int fusb30x_probe(struct i2c_client *client, fusb_initialize_timer(chip); - chip->fusb30x_wq = create_workqueue("fusb302_wq"); - INIT_WORK(&chip->work, fusb302_work_func); + chip->irq_worker = kthread_create_worker(0, dev_name(chip->dev)); + if (IS_ERR(chip->irq_worker)) + return PTR_ERR(chip->irq_worker); + sched_setscheduler_nocheck(chip->irq_worker->task, SCHED_FIFO, ¶m); + kthread_init_work(&chip->irq_work, fusb302_work_func); chip->role = ROLE_MODE_NONE; chip->try_role = ROLE_MODE_NONE; @@ -3369,6 +3427,8 @@ static int fusb30x_probe(struct i2c_client *client, chip->n_caps_used = 1; chip->source_power_supply[0] = 0x64; chip->source_max_current[0] = 0x96; + chip->sink_volt = 100; + chip->sink_opr_cur = 200; pd_cap_info = &chip->pd_cap_info; pd_cap_info->dual_role_power = 1; @@ -3469,13 +3529,8 @@ static int fusb30x_probe(struct i2c_client *client, goto IRQ_ERR; } - ret = devm_request_threaded_irq(&client->dev, - chip->gpio_int_irq, - NULL, - cc_interrupt_handler, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - client->name, - chip); + ret = request_irq(chip->gpio_int_irq, cc_interrupt_handler, + IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); if (ret) { dev_err(&client->dev, "irq request failed\n"); goto IRQ_ERR; @@ -3504,7 +3559,7 @@ static int fusb30x_probe(struct i2c_client *client, } return 0; IRQ_ERR: - destroy_workqueue(chip->fusb30x_wq); + kthread_destroy_worker(chip->irq_worker); return ret; } @@ -3512,7 +3567,8 @@ static int fusb30x_remove(struct i2c_client *client) { struct fusb30x_chip *chip = i2c_get_clientdata(client); - destroy_workqueue(chip->fusb30x_wq); + free_irq(chip->gpio_int_irq, chip); + kthread_destroy_worker(chip->irq_worker); return 0; } @@ -3535,7 +3591,7 @@ static int fusb30x_pm_suspend(struct device *dev) fusb_irq_disable(chip); chip->suspended = true; - cancel_work_sync(&chip->work); + kthread_cancel_work_sync(&chip->irq_work); return 0; } @@ -3546,7 +3602,7 @@ static int fusb30x_pm_resume(struct device *dev) fusb_irq_enable(chip); chip->suspended = false; - queue_work(chip->fusb30x_wq, &chip->work); + kthread_queue_work(chip->irq_worker, &chip->irq_work); return 0; } diff --git a/drivers/mfd/fusb302.h b/drivers/mfd/fusb302.h index 3f40abfbd482..9b162f5d4246 100644 --- a/drivers/mfd/fusb302.h +++ b/drivers/mfd/fusb302.h @@ -482,8 +482,8 @@ struct fusb30x_chip { struct i2c_client *client; struct device *dev; struct regmap *regmap; - struct work_struct work; - struct workqueue_struct *fusb30x_wq; + struct kthread_work irq_work; + struct kthread_worker *irq_worker; struct hrtimer timer_state_machine; struct hrtimer timer_mux_machine; struct PD_CAP_INFO pd_cap_info; @@ -525,10 +525,19 @@ struct fusb30x_chip { int msg_id; enum tx_state tx_state; int hardrst_count; - u32 source_power_supply[7]; /* 50mv unit */ - u32 source_max_current[7]; + u32 source_power_supply[7]; /* 10ma uint*/ + u32 source_max_current[7]; + /* Fixed supply = 0, Battery supply = 1 */ + u32 sink_supply_type; + /* Sink Fixed Supply */ + u32 sink_volt; + u32 sink_opr_cur; + /* Sink Battery Supply */ + u32 sink_max_volt; + u32 sink_min_volt; + u32 sink_opr_power; int pos_power; /* * if PartnerCap[0] == 0xffffffff