summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorzain wang <wzz@rock-chips.com>2018-03-15 17:24:27 +0800
committerTao Huang <huangtao@rock-chips.com>2018-03-30 14:16:37 +0800
commitfdcef14e49cf877d3a51237dbe82424b5dff5c77 (patch)
treed3ebcb81cc12ba5cccb859c36dbf7ed55e64c73b /drivers/mfd
parent2a3bd5ca5b420fe7bea0c352909f34846a4dedb8 (diff)
mfd/fusb302: support power role swap in DRP port
Change-Id: I233023de022eea2ff003f1af2b1763b056af25d7 Signed-off-by: zain wang <wzz@rock-chips.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/fusb302.c453
-rw-r--r--drivers/mfd/fusb302.h20
2 files changed, 421 insertions, 52 deletions
diff --git a/drivers/mfd/fusb302.c b/drivers/mfd/fusb302.c
index 60a5102edd89..4e32ccc3adcb 100644
--- a/drivers/mfd/fusb302.c
+++ b/drivers/mfd/fusb302.c
@@ -1153,6 +1153,7 @@ static int vdm_send_discoveryid(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_DISCOVERY_ID send failed\n");
/* disable auto_vdm_machine */
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1165,6 +1166,7 @@ static int vdm_send_discoveryid(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
@@ -1193,6 +1195,7 @@ static int vdm_send_discoverysvid(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS send failed\n");
/* disable auto_vdm_machine */
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1205,6 +1208,7 @@ static int vdm_send_discoverysvid(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
@@ -1233,6 +1237,7 @@ static int vdm_send_discoverymodes(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev,
"VDM_DISCOVERY_MODES send failed\n");
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1248,6 +1253,7 @@ static int vdm_send_discoverymodes(struct fusb30x_chip *chip, int evt)
"VDM_DISCOVERY_MODES time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
@@ -1280,6 +1286,7 @@ static int vdm_send_entermode(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_ENTER_MODE send failed\n");
/* disable auto_vdm_machine */
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1293,6 +1300,7 @@ static int vdm_send_entermode(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_ENTER_MODE time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
@@ -1320,6 +1328,7 @@ static int vdm_send_getdpstatus(struct fusb30x_chip *chip, int evt)
"VDM_DP_STATUS_UPDATE send failed\n");
/* disable auto_vdm_machine */
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1333,6 +1342,7 @@ static int vdm_send_getdpstatus(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
@@ -1359,6 +1369,7 @@ static int vdm_send_dpconfig(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "vdm_send_dpconfig send failed\n");
/* disable auto_vdm_machine */
chip->vdm_state = 0xff;
+ return -EPIPE;
}
if (chip->vdm_send_state != 2)
@@ -1372,45 +1383,55 @@ static int vdm_send_dpconfig(struct fusb30x_chip *chip, int evt)
dev_warn(chip->dev, "vdm_send_dpconfig time out\n");
chip->vdm_state = 0xff;
chip->work_continue = 1;
+ return -ETIMEDOUT;
}
break;
}
return -EINPROGRESS;
}
+/* without break if success */
+#define AUTO_VDM_HANDLE(func, chip, evt, conditions) \
+do { \
+ conditions = func(chip, evt); \
+ if (!conditions) { \
+ chip->vdm_state++; \
+ chip->work_continue = 1; \
+ } else { \
+ if (conditions != -EINPROGRESS) \
+ chip->vdm_state = 0xff; \
+ } \
+} while (0)
+
static void auto_vdm_machine(struct fusb30x_chip *chip, int evt)
{
+ int conditions;
+
switch (chip->vdm_state) {
case 0:
- if (vdm_send_discoveryid(chip, evt))
- break;
- chip->vdm_state++;
- /* without break */
+ AUTO_VDM_HANDLE(vdm_send_discoveryid, chip, evt, conditions);
+ break;
case 1:
- if (vdm_send_discoverysvid(chip, evt))
- break;
- chip->vdm_state++;
- /* without break */
+ AUTO_VDM_HANDLE(vdm_send_discoverysvid, chip, evt, conditions);
+ break;
case 2:
- if (vdm_send_discoverymodes(chip, evt))
- break;
- chip->vdm_state++;
- /* without break */
+ AUTO_VDM_HANDLE(vdm_send_discoverymodes, chip, evt, conditions);
+ break;
case 3:
- if (vdm_send_entermode(chip, evt))
- break;
- chip->vdm_state++;
- /* without break */
+ AUTO_VDM_HANDLE(vdm_send_entermode, chip, evt, conditions);
+ break;
case 4:
- if (vdm_send_dpconfig(chip, evt))
- break;
- chip->vdm_state = 6;
- /* without break */
+ AUTO_VDM_HANDLE(vdm_send_dpconfig, chip, evt, conditions);
+ break;
case 5:
- if (vdm_send_getdpstatus(chip, evt))
- break;
- chip->vdm_state++;
- /* without break */
+ platform_fusb_notify(chip);
+ break;
+ case 6:
+ /* TODO: triggered by user */
+ AUTO_VDM_HANDLE(vdm_send_getdpstatus, chip, evt, conditions);
+ if (chip->vdm_state == 7)
+ chip->vdm_state = 5;
+ break;
default:
platform_fusb_notify(chip);
break;
@@ -1773,17 +1794,159 @@ static void fusb_state_src_ready(struct fusb30x_chip *chip, int evt)
process_vdm_msg(chip);
chip->work_continue = 1;
chip->timer_state = T_DISABLED;
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PR_SWAP)) {
+ if ((chip->vdm_state == 5) || (chip->vdm_state == 0xff))
+ set_state(chip, policy_src_prs_evaluate);
+ return;
}
}
- /* TODO: swap function would be added here later on*/
-
if (!chip->partner_cap[0])
set_state(chip, policy_src_get_sink_caps);
- else
+ else if ((chip->vdm_state != 5) && (chip->vdm_state != 0xff))
auto_vdm_machine(chip, evt);
}
+static void fusb_state_prs_evaluate(struct fusb30x_chip *chip, int evt)
+{
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_accept);
+ else
+ set_state(chip, policy_src_prs_reject);
+}
+
+static void fusb_state_prs_reject(struct fusb30x_chip *chip, int evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_REJECT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ } else if (tmp == tx_failed) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_softrst);
+ else
+ set_state(chip, policy_snk_send_softrst);
+ }
+ }
+}
+
+static void fusb_state_prs_accept(struct fusb30x_chip *chip, int evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ if (chip->notify.power_role)
+ set_state(chip,
+ policy_src_prs_transition_to_off);
+ else
+ set_state(chip,
+ policy_snk_prs_transition_to_off);
+ } else if (tmp == tx_failed) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_softrst);
+ else
+ set_state(chip, policy_snk_send_softrst);
+ }
+ }
+}
+
+static void fusb_state_src_prs_transition_to_off(struct fusb30x_chip *chip,
+ int evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_SRC_TRANSITION;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ break;
+ case 1:
+ if (evt & EVENT_TIMER_STATE) {
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ chip->notify.power_role = 0;
+ tcpm_set_msg_header(chip);
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_assert_rd);
+ else
+ set_state(chip, policy_src_prs_source_off);
+ }
+ }
+}
+
+static void fusb_state_src_prs_assert_rd(struct fusb30x_chip *chip, int evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_DOWN);
+ set_state(chip, policy_src_prs_source_off);
+}
+
+static void fusb_state_src_prs_source_off(struct fusb30x_chip *chip, int evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = 1;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ if (chip->sub_state != 3)
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ chip->timer_state = T_DISABLED;
+ /* snk startup */
+ chip->notify.is_pd_connected = 0;
+ chip->cc_state |= CC_STATE_TOGSS_IS_UFP;
+ tcpm_set_polarity(chip, chip->cc_polarity);
+ tcpm_set_rx_enable(chip, 1);
+ set_state(chip, policy_snk_discovery);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = 1;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ }
+}
+
static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, int evt)
{
u32 tmp;
@@ -2120,7 +2283,10 @@ static void fusb_state_snk_transition_default(struct fusb30x_chip *chip,
static void fusb_state_snk_ready(struct fusb30x_chip *chip, int evt)
{
- /* TODO: snk_ready_function would be added later on*/
+ if (evt & EVENT_RX)
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PR_SWAP))
+ set_state(chip, policy_snk_prs_evaluate);
+
platform_fusb_notify(chip);
}
@@ -2144,6 +2310,144 @@ static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, int evt)
}
}
+static void fusb_state_prs_send_swap(struct fusb30x_chip *chip, int evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_PR_SWAP, CONTROLMESSAGE);
+ chip->sub_state = 1;
+ chip->tx_state = tx_idle;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_softrst);
+ else
+ set_state(chip, policy_snk_send_softrst);
+ }
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_ACCEPT)) {
+ tcpm_set_msg_header(chip);
+ chip->timer_state = T_DISABLED;
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_prs_transition_to_off);
+ else
+ set_state(chip, policy_snk_prs_transition_to_off);
+ chip->notify.power_role = 1;
+ tcpm_set_msg_header(chip);
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_REJECT) ||
+ PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_WAIT)) {
+ chip->timer_state = T_DISABLED;
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_prs_transition_to_off(struct fusb30x_chip *chip,
+ int evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_PD_SOURCE_OFF;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip,
+ policy_snk_prs_assert_rp);
+ else
+ set_state(chip,
+ policy_snk_prs_source_on);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = 0;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_prs_assert_rp(struct fusb30x_chip *chip, int evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ set_state(chip, policy_snk_prs_source_on);
+}
+
+static void fusb_state_snk_prs_source_on(struct fusb30x_chip *chip, int evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ /* supply power in 50ms */
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ chip->sub_state++;
+ chip->work_continue = 1;
+ break;
+ case 1:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 2:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ /* PD spe 6.5.10.2 */
+ chip->timer_state = T_PD_SWAP_SOURCE_START;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = 0;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ case 3:
+ if (evt & EVENT_TIMER_STATE) {
+ chip->cc_state &= ~CC_STATE_TOGSS_IS_UFP;
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK,
+ MASK_M_COMP_CHNG, 0);
+ set_state(chip, policy_src_send_caps);
+ }
+ break;
+ }
+}
+
static void fusb_state_snk_softreset(struct fusb30x_chip *chip)
{
u32 tmp;
@@ -2209,33 +2513,46 @@ static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, int evt)
}
}
+static void fusb_try_detach(struct fusb30x_chip *chip)
+{
+ int cc1, cc2;
+
+ if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) &&
+ (chip->conn_state !=
+ policy_snk_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_send_swap) &&
+ (chip->conn_state != policy_snk_prs_assert_rp) &&
+ (chip->conn_state != policy_snk_prs_source_on) &&
+ (chip->conn_state != policy_snk_prs_transition_to_off)) {
+ if (!tcpm_check_vbus(chip))
+ set_state_unattached(chip);
+ } else if ((chip->conn_state !=
+ policy_src_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_source_on)) {
+ tcpm_get_cc(chip, &cc1, &cc2);
+ if (chip->cc_state & CC_STATE_TOGSS_CC2)
+ cc1 = cc2;
+ if (cc1 == TYPEC_CC_VOLT_OPEN)
+ set_state_unattached(chip);
+ }
+}
+
static void state_machine_typec(struct fusb30x_chip *chip)
{
int evt = 0;
- int cc1, cc2;
tcpc_alert(chip, &evt);
mux_alert(chip, &evt);
if (!evt)
goto BACK;
- if (chip->notify.is_cc_connected) {
- if (evt & EVENT_CC) {
- if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) &&
- (chip->conn_state !=
- policy_snk_transition_default)) {
- if (!tcpm_check_vbus(chip))
- set_state_unattached(chip);
- } else if (chip->conn_state !=
- policy_src_transition_default) {
- tcpm_get_cc(chip, &cc1, &cc2);
- if (chip->cc_state & CC_STATE_TOGSS_CC2)
- cc1 = cc2;
- if (cc1 == TYPEC_CC_VOLT_OPEN)
- set_state_unattached(chip);
- }
- }
- }
+ if (chip->notify.is_cc_connected)
+ if (evt & EVENT_CC)
+ fusb_try_detach(chip);
if (evt & EVENT_RX) {
tcpm_get_message(chip);
@@ -2348,6 +2665,42 @@ static void state_machine_typec(struct fusb30x_chip *chip)
fusb_state_snk_softreset(chip);
break;
+ /* PD Spec 1.0 chap 8.3.3.6.3.1/2 */
+ case policy_src_prs_evaluate:
+ case policy_snk_prs_evaluate:
+ fusb_state_prs_evaluate(chip, evt);
+ break;
+ case policy_snk_prs_accept:
+ case policy_src_prs_accept:
+ fusb_state_prs_accept(chip, evt);
+ break;
+ case policy_snk_prs_reject:
+ case policy_src_prs_reject:
+ fusb_state_prs_reject(chip, evt);
+ break;
+ case policy_src_prs_transition_to_off:
+ fusb_state_src_prs_transition_to_off(chip, evt);
+ break;
+ case policy_src_prs_assert_rd:
+ fusb_state_src_prs_assert_rd(chip, evt);
+ break;
+ case policy_src_prs_source_off:
+ fusb_state_src_prs_source_off(chip, evt);
+ break;
+ case policy_snk_prs_send_swap:
+ case policy_src_prs_send_swap:
+ fusb_state_prs_send_swap(chip, evt);
+ break;
+ case policy_snk_prs_transition_to_off:
+ fusb_state_snk_prs_transition_to_off(chip, evt);
+ break;
+ case policy_snk_prs_assert_rp:
+ fusb_state_snk_prs_assert_rp(chip, evt);
+ break;
+ case policy_snk_prs_source_on:
+ fusb_state_snk_prs_source_on(chip, evt);
+ break;
+
default:
break;
}
@@ -2515,13 +2868,9 @@ static int fusb30x_probe(struct i2c_client *client,
chip->source_power_supply[0] = 0x64;
chip->source_max_current[0] = 0x96;
- /*
- * these two variable should be 1 if support DRP,
- * but now we do not support swap,
- * it will be blanked in future
- */
pd_cap_info = &chip->pd_cap_info;
- pd_cap_info->dual_role_power = 0;
+ pd_cap_info->dual_role_power = 1;
+ /* TODO: add date role swap */
pd_cap_info->data_role_swap = 0;
pd_cap_info->externally_powered = 1;
diff --git a/drivers/mfd/fusb302.h b/drivers/mfd/fusb302.h
index b726698f58e2..2f4a44ae9b97 100644
--- a/drivers/mfd/fusb302.h
+++ b/drivers/mfd/fusb302.h
@@ -90,6 +90,23 @@ enum connection_state {
policy_snk_send_hardrst,
policy_snk_transition_default,
+
+ /* PR SWAP */
+ policy_src_prs_evaluate,
+ policy_src_prs_accept,
+ policy_src_prs_transition_to_off,
+ policy_src_prs_source_off,
+ policy_src_prs_assert_rd,
+ policy_src_prs_reject,
+ policy_src_prs_send_swap,
+
+ policy_snk_prs_evaluate,
+ policy_snk_prs_accept,
+ policy_snk_prs_transition_to_off,
+ policy_snk_prs_source_on,
+ policy_snk_prs_assert_rp,
+ policy_snk_prs_reject,
+ policy_snk_prs_send_swap,
};
enum tcpm_rp_value {
@@ -312,6 +329,9 @@ enum role_mode {
#define T_SAFE_0V 650
#define T_SRC_TURN_ON 275
#define T_SRC_RECOVER_MAX 1000
+#define T_PD_SOURCE_OFF 920
+#define T_PD_SOURCE_ON 480
+#define T_PD_SWAP_SOURCE_START 20
#define T_NO_TRIGGER 500
#define T_DISABLED 0xffff