summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-11 02:48:51 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-11 02:48:51 +0000
commitb7184daf422670e59797137e4fe83cbfd9f1af84 (patch)
tree318f705643bdfeaf71d19a5a8bc78008b3418edc
parent2f04cd37cc49d326a4a04d114facf40efa5423e2 (diff)
parent1afb79d9dd091871bf0ec69002f07e2d933c13f8 (diff)
downloadtangorpro-android-gs-felix-5.10-u-beta5.2.tar.gz
Change-Id: Ib951e8f0ffbc22647db0b64ace123ff63ace53f5
-rw-r--r--dts/gs201-pogo-transport-dev.dtsi2
-rw-r--r--dts/gs201-pogo-transport-evt2.dtsi1
-rw-r--r--pogo/pogo_transport.c1476
3 files changed, 1451 insertions, 28 deletions
diff --git a/dts/gs201-pogo-transport-dev.dtsi b/dts/gs201-pogo-transport-dev.dtsi
index 4d37ede..f874cee 100644
--- a/dts/gs201-pogo-transport-dev.dtsi
+++ b/dts/gs201-pogo-transport-dev.dtsi
@@ -16,6 +16,8 @@
/delete-property/ pogo-acc-capable;
/delete-property/ usb-hub-supply;
/delete-property/ acc-detect-supply;
+
+ legacy-event-driven;
};
};
};
diff --git a/dts/gs201-pogo-transport-evt2.dtsi b/dts/gs201-pogo-transport-evt2.dtsi
index b33cd6e..e46f9f2 100644
--- a/dts/gs201-pogo-transport-evt2.dtsi
+++ b/dts/gs201-pogo-transport-evt2.dtsi
@@ -15,6 +15,7 @@
google_pogo: google,pogo {
/delete-property/ pogo-acc-capable;
pogo-acc-hall-only;
+ legacy-event-driven;
};
};
};
diff --git a/pogo/pogo_transport.c b/pogo/pogo_transport.c
index ddd863e..dc27571 100644
--- a/pogo/pogo_transport.c
+++ b/pogo/pogo_transport.c
@@ -20,6 +20,8 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
#include <linux/usb/tcpm.h>
#include <misc/gvotable.h>
@@ -38,10 +40,94 @@
#define KEEP_USB_PATH 2
#define KEEP_HUB_PATH 2
+#define DEFAULT_STATE_MACHINE_ENABLE true
#define POGO_VOTER "POGO"
#define SSPHY_RESTART_EL "SSPHY_RESTART"
+/*
+ * State Description:
+ * INVALID_STATE,
+ * (A) STANDBY, // Nothing attached, hub disabled
+ * DOCKING_DEBOUNCED, // STANDBY -> DOCK_HUB, pogo gpio
+ * STANDBY_ACC_DEBOUNCED, // STANDBY -> ACC_DIRECT, acc gpio
+ * (B) DOCK_HUB, // Dock online, hub enabled
+ * (C) DOCK_DEVICE_HUB, // Dock online, usb device online, hub enabled
+ * (H) DOCK_AUDIO_HUB, // Dock online, usb audio online, hub enabled
+ * (I) AUDIO_HUB, // Usb audio online, hub enabled
+ * AUDIO_HUB_DOCKING_DEBOUNCED, // AUDIO_HUB -> DOCK_AUDIO_HUB, pogo gpio
+ * AUDIO_HUB_ACC_DEBOUNCED, // AUDIO_HUB -> ACC_AUDIO_HUB, acc gpio
+ * (D) DEVICE_HUB, // Usb device online, hub enabled
+ * DEVICE_HUB_DOCKING_DEBOUNCED, // DEVICE_HUB -> DOCK_DEVICE_HUB, pogo gpio
+ * DEVICE_HUB_ACC_DEBOUNCED, // DEVICE_HUB -> ACC_DEVICE_HUB, acc gpio
+ * (E) DEVICE_DIRECT, // Usb device online, hub disabled
+ * DEVICE_DOCKING_DEBOUNCED, // DEVICE_DIRECT -> DOCK_DEVICE_HUB, pogo gpio
+ * DEVICE_DIRECT_ACC_DEBOUNCED, // DEVICE_DIRECT -> ACC_DEVICE_HUB, acc gpio
+ * (F) AUDIO_DIRECT, // Usb audio online, hub disabled
+ * AUDIO_DIRECT_DOCKING_DEBOUNCED, // AUDIO_DIRECT -> AUDIO_DIRECT_DOCK_OFFLINE, pogo gpio
+ * AUDIO_DIRECT_ACC_DEBOUNCED, // AUDIO_DIRECT -> ACC_DEVICE_HUB, acc gpio
+ * (G) AUDIO_DIRECT_DOCK_OFFLINE, // Usb audio online, dock offline, hub disabled
+ * (J) HOST_DIRECT, // Usb host online, hub disabled
+ * HOST_DIRECT_DOCKING_DEBOUNCED, // HOST_DIRECT -> HOST_DIRECT_DOCK_OFFLINE, pogo gpio
+ * (K) HOST_DIRECT_DOCK_OFFLINE, // Usb host online, dock offline, hub disabled
+ * HOST_DIRECT_ACC_DEBOUNCED, // HOST_DIRECT -> HOST_DIRECT_ACC_OFFLINE, acc gpio
+ * (L) DOCK_HUB_HOST_OFFLINE, // Dock online, usb host offline, hub enabled
+ * (M) ACC_DIRECT, // Acc online, hub disabled
+ * (N) ACC_DEVICE_HUB, // Acc online, usb device online, hub enabled
+ * (O) ACC_HUB, // Acc online, hub enabled
+ * (T) ACC_HUB_HOST_OFFLINE, // Acc online, hub enabled, usb host offline
+ * (P) ACC_AUDIO_HUB, // Acc online, usb audio online, hub enabled
+ * (Q) LID_CLOSE,
+ * (R) HOST_DIRECT_ACC_OFFLINE, // Usb host online, acc offline, hub disabled
+ * (S) ACC_DIRECT_HOST_OFFLINE, // Acc online, usb host offline
+ */
+
+#define FOREACH_STATE(S) \
+ S(INVALID_STATE), \
+ S(STANDBY), \
+ S(DOCKING_DEBOUNCED), \
+ S(STANDBY_ACC_DEBOUNCED), \
+ S(DOCK_HUB), \
+ S(DOCK_DEVICE_HUB), \
+ S(DOCK_AUDIO_HUB), \
+ S(AUDIO_HUB), \
+ S(AUDIO_HUB_DOCKING_DEBOUNCED), \
+ S(AUDIO_HUB_ACC_DEBOUNCED), \
+ S(DEVICE_HUB), \
+ S(DEVICE_HUB_DOCKING_DEBOUNCED), \
+ S(DEVICE_HUB_ACC_DEBOUNCED), \
+ S(DEVICE_DIRECT), \
+ S(DEVICE_DOCKING_DEBOUNCED), \
+ S(DEVICE_DIRECT_ACC_DEBOUNCED), \
+ S(AUDIO_DIRECT), \
+ S(AUDIO_DIRECT_DOCKING_DEBOUNCED), \
+ S(AUDIO_DIRECT_ACC_DEBOUNCED), \
+ S(AUDIO_DIRECT_DOCK_OFFLINE), \
+ S(HOST_DIRECT), \
+ S(HOST_DIRECT_DOCKING_DEBOUNCED), \
+ S(HOST_DIRECT_DOCK_OFFLINE), \
+ S(HOST_DIRECT_ACC_DEBOUNCED), \
+ S(DOCK_HUB_HOST_OFFLINE), \
+ S(ACC_DIRECT), \
+ S(ACC_DEVICE_HUB), \
+ S(ACC_HUB), \
+ S(ACC_HUB_HOST_OFFLINE), \
+ S(ACC_AUDIO_HUB), \
+ S(LID_CLOSE), \
+ S(HOST_DIRECT_ACC_OFFLINE), \
+ S(ACC_DIRECT_HOST_OFFLINE)
+
+#define GENERATE_ENUM(e) e
+#define GENERATE_STRING(s) #s
+
+enum pogo_state {
+ FOREACH_STATE(GENERATE_ENUM)
+};
+
+static const char * const pogo_states[] = {
+ FOREACH_STATE(GENERATE_STRING)
+};
+
enum pogo_event_type {
/* Reported when docking status changes */
EVENT_DOCKING,
@@ -72,6 +158,16 @@ enum pogo_event_type {
EVENT_ORIENTATION_CHANGED,
};
+#define EVENT_POGO_IRQ BIT(0)
+#define EVENT_USBC_DATA_CHANGE BIT(1)
+#define EVENT_ENABLE_USB_DATA BIT(2)
+#define EVENT_HES_H1S_CHANGED BIT(3)
+#define EVENT_ACC_GPIO_ACTIVE BIT(4)
+#define EVENT_ACC_CONNECTED BIT(5)
+#define EVENT_AUDIO_DEV_ATTACHED BIT(6)
+#define EVENT_USBC_ORIENTATION BIT(7)
+#define EVENT_LAST_EVENT_TYPE BIT(63)
+
static bool modparam_force_usb;
module_param_named(force_usb, modparam_force_usb, bool, 0644);
MODULE_PARM_DESC(force_usb, "Force enabling usb path over pogo");
@@ -81,6 +177,15 @@ static int modparam_pogo_accessory_enable;
module_param_named(pogo_accessory_enable, modparam_pogo_accessory_enable, int, 0644);
MODULE_PARM_DESC(pogo_accessory_enable, "Enabling accessory detection over pogo");
+/* Set to 1 (enable) or 2 (disable) to override the default value */
+static int modparam_state_machine_enable;
+module_param_named(state_machine_enable, modparam_state_machine_enable, int, 0644);
+MODULE_PARM_DESC(state_machine_enable, "Enabling pogo state machine transition");
+
+extern void register_bus_suspend_callback(void (*callback)(void *bus_suspend_payload, bool main_hcd,
+ bool suspend),
+ void *data);
+
struct pogo_event {
struct kthread_delayed_work work;
struct pogo_transport *pogo_transport;
@@ -105,6 +210,11 @@ enum pogo_accessory_detection {
ENABLED
};
+struct pogo_transport_udev_ids {
+ __le16 vendor;
+ __le16 product;
+};
+
struct pogo_transport {
struct device *dev;
struct max77759_plat *chip;
@@ -153,7 +263,31 @@ struct pogo_transport {
* Only applicable for debugfs capable builds.
*/
bool mock_hid_connected;
+ /* When true, lid is closed */
+ bool lid_closed;
+ /* When true, the bus has not yet suspended after the lid is closed. */
+ bool pending_first_suspend;
+
struct kthread_worker *wq;
+ struct kthread_delayed_work state_machine;
+ struct kthread_work event_work;
+ enum pogo_state prev_state;
+ enum pogo_state state;
+ enum pogo_state delayed_state;
+ unsigned long delayed_runtime;
+ unsigned long delay_ms;
+ unsigned long event_map;
+ bool state_machine_running;
+ bool state_machine_enabled;
+ spinlock_t pogo_event_lock;
+
+ /* Register the notifier from USB core */
+ struct notifier_block udev_nb;
+ /* When true, a superspeed (or better) USB device is enumerated */
+ bool ss_udev_attached;
+ bool main_hcd_suspend;
+ bool shared_hcd_suspend;
+
/* To read voltage at the pogo pins */
struct power_supply *pogo_psy;
/* Retry when voltage is less than POGO_USB_CAPABLE_THRESHOLD_UV */
@@ -173,6 +307,10 @@ struct pogo_transport {
/* Orientation of USB-C, 0:TYPEC_POLARITY_CC1 1:TYPEC_POLARITY_CC2 */
enum typec_cc_polarity polarity;
+
+ /* Cache values from the Type-C driver */
+ enum typec_data_role usbc_data_role;
+ bool usbc_data_active;
};
static const unsigned int pogo_extcon_cable[] = {
@@ -181,6 +319,32 @@ static const unsigned int pogo_extcon_cable[] = {
EXTCON_NONE,
};
+/*
+ * list of USB VID:PID pair of udevs which are audio docks with pogo interfaces
+ * Both VID and PID are required in each entry.
+ */
+static const struct pogo_transport_udev_ids audio_dock_ids[] = {
+ {
+ .vendor = cpu_to_le16(0x18d1),
+ .product = cpu_to_le16(0x9480),
+ },
+ { },
+};
+
+/* Return true if @vid and @pid pair is found in @list. Otherwise, return false. */
+static bool pogo_transport_match_udev(const struct pogo_transport_udev_ids *list, const u16 vid,
+ const u16 pid)
+{
+ if (list) {
+ while (list->vendor && list->product) {
+ if (list->vendor == cpu_to_le16(vid) && list->product == cpu_to_le16(pid))
+ return true;
+ list++;
+ }
+ }
+ return false;
+}
+
static void pogo_transport_event(struct pogo_transport *pogo_transport,
enum pogo_event_type event_type, int delay_ms);
@@ -190,22 +354,21 @@ static void update_extcon_dev(struct pogo_transport *pogo_transport, bool docked
/* While docking, Signal EXTCON_USB before signalling EXTCON_DOCK */
if (docked) {
- ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_USB, usb_capable ?
- 1 : 0);
+ ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_USB, usb_capable);
if (ret)
dev_err(pogo_transport->dev, "%s Failed to %s EXTCON_USB\n", __func__,
usb_capable ? "set" : "clear");
- ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_DOCK, 1);
+ ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_DOCK, true);
if (ret)
dev_err(pogo_transport->dev, "%s Failed to set EXTCON_DOCK\n", __func__);
return;
}
/* b/241919179: While undocking, Signal EXTCON_DOCK before signalling EXTCON_USB */
- ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_DOCK, 0);
+ ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_DOCK, false);
if (ret)
dev_err(pogo_transport->dev, "%s Failed to clear EXTCON_DOCK\n", __func__);
- ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_USB, 0);
+ ret = extcon_set_state_sync(pogo_transport->extcon, EXTCON_USB, false);
if (ret)
dev_err(pogo_transport->dev, "%s Failed to clear EXTCON_USB\n", __func__);
}
@@ -222,6 +385,7 @@ static void ssphy_restart_control(struct pogo_transport *pogo_transport, bool en
return;
}
+ logbuffer_log(pogo_transport->log, "ssphy_restart_control %u", enable);
gvotable_cast_long_vote(pogo_transport->ssphy_restart_votable, POGO_VOTER, enable, enable);
}
@@ -306,6 +470,8 @@ static void switch_to_usbc_locked(struct pogo_transport *pogo_transport)
pogo_transport_update_polarity(pogo_transport, TYPEC_POLARITY_CC2, false);
enable_data_path_locked(chip);
+ /* pogo_transport->pogo_usb_active updated. Delaying till usb-c is activated. */
+ kobject_uevent(&pogo_transport->dev->kobj, KOBJ_CHANGE);
}
static void switch_to_pogo_locked(struct pogo_transport *pogo_transport)
@@ -338,6 +504,8 @@ static void switch_to_pogo_locked(struct pogo_transport *pogo_transport)
logbuffer_log(pogo_transport->log, "%s: %s turning on host for Pogo", __func__, ret < 0 ?
"Failed" : "Succeeded");
pogo_transport->pogo_usb_active = true;
+ /* pogo_transport->pogo_usb_active updated */
+ kobject_uevent(&pogo_transport->dev->kobj, KOBJ_CHANGE);
}
static void switch_to_hub_locked(struct pogo_transport *pogo_transport)
@@ -369,6 +537,11 @@ static void switch_to_hub_locked(struct pogo_transport *pogo_transport)
ret = extcon_set_state_sync(chip->extcon, EXTCON_USB_HOST, 0);
logbuffer_log(pogo_transport->log, "%s: %s turning off host for Pogo", __func__,
ret < 0 ? "Failed" : "Succeeded");
+ /*
+ * Skipping KOBJ_CHANGE here as it's a transient state. Should be changed if the
+ * function logic changes to having branches to exit the function before
+ * pogo_usb_active to true.
+ */
pogo_transport->pogo_usb_active = false;
}
@@ -406,9 +579,10 @@ static void switch_to_hub_locked(struct pogo_transport *pogo_transport)
logbuffer_log(pogo_transport->log, "%s: %s turning on host for hub", __func__, ret < 0 ?
"Failed" : "Succeeded");
- /* TODO: re-design the flags */
pogo_transport->pogo_usb_active = true;
pogo_transport->pogo_hub_active = true;
+ /* pogo_transport->pogo_usb_active updated.*/
+ kobject_uevent(&pogo_transport->dev->kobj, KOBJ_CHANGE);
}
static void update_pogo_transport(struct pogo_transport *pogo_transport,
@@ -748,6 +922,1055 @@ static void pogo_transport_event(struct pogo_transport *pogo_transport,
kthread_mod_delayed_work(pogo_transport->wq, &evt->work, msecs_to_jiffies(delay_ms));
}
+/*-------------------------------------------------------------------------*/
+/* State Machine Functions */
+/*-------------------------------------------------------------------------*/
+
+/*
+ * State transition
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_set_state(struct pogo_transport *pogo_transport, enum pogo_state state,
+ unsigned int delay_ms)
+{
+ if (delay_ms) {
+ logbuffer_log(pogo_transport->log, "pending state change %s -> %s @ %u ms",
+ pogo_states[pogo_transport->state], pogo_states[state], delay_ms);
+ pogo_transport->delayed_state = state;
+ kthread_mod_delayed_work(pogo_transport->wq, &pogo_transport->state_machine,
+ msecs_to_jiffies(delay_ms));
+ pogo_transport->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms);
+ pogo_transport->delay_ms = delay_ms;
+ } else {
+ logbuffer_logk(pogo_transport->log, LOGLEVEL_INFO, "state change %s -> %s",
+ pogo_states[pogo_transport->state], pogo_states[state]);
+ pogo_transport->delayed_state = INVALID_STATE;
+ pogo_transport->prev_state = pogo_transport->state;
+ pogo_transport->state = state;
+
+ if (!pogo_transport->state_machine_running)
+ kthread_mod_delayed_work(pogo_transport->wq, &pogo_transport->state_machine,
+ 0);
+ }
+}
+
+/*
+ * Accessory Detection regulator control
+ * - Return -ENXIO if Accessory Detection regulator does not exist
+ * - Return 0 if @enable is the same as the status of the regulator
+ * - Otherwise, return the return value from regulator_enable or regulator_disable
+ */
+static int pogo_transport_acc_regulator(struct pogo_transport *pogo_transport, bool enable)
+{
+ int ret;
+
+ if (!pogo_transport->acc_detect_ldo)
+ return -ENXIO;
+
+ if (regulator_is_enabled(pogo_transport->acc_detect_ldo) == enable)
+ return 0;
+
+ if (enable)
+ ret = regulator_enable(pogo_transport->acc_detect_ldo);
+ else
+ ret = regulator_disable(pogo_transport->acc_detect_ldo);
+
+ return ret;
+}
+
+/*
+ * Call this function to:
+ * - Disable POGO Vout by voting 0 to charger_mode_votable
+ * - Disable the regulator for Accessory Detection Logic
+ * - Disable Accessory Detection IRQ
+ * - Enable POGO Voltage Detection IRQ
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_reset_acc_detection(struct pogo_transport *pogo_transport)
+{
+ int ret;
+
+ ret = gvotable_cast_long_vote(pogo_transport->charger_mode_votable, POGO_VOTER,
+ GBMS_POGO_VOUT, 0);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to unvote VOUT, ret %d", __func__,
+ ret);
+
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d", __func__,
+ ret);
+
+ if (pogo_transport->acc_irq_enabled) {
+ disable_irq(pogo_transport->pogo_acc_irq);
+ pogo_transport->acc_irq_enabled = false;
+ }
+
+ if (!pogo_transport->pogo_irq_enabled) {
+ enable_irq(pogo_transport->pogo_irq);
+ pogo_transport->pogo_irq_enabled = true;
+ }
+}
+
+/*
+ * This function implements the actions unon entering each state.
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_run_state_machine(struct pogo_transport *pogo_transport)
+{
+ bool acc_detected = gpio_get_value(pogo_transport->pogo_acc_gpio);
+ bool docked = !gpio_get_value(pogo_transport->pogo_gpio);
+ struct max77759_plat *chip = pogo_transport->chip;
+ int ret;
+
+ switch (pogo_transport->state) {
+ case STANDBY:
+ /* DATA_STATUS_ENABLED */
+ break;
+ case DOCKING_DEBOUNCED:
+ if (docked) {
+ switch_to_hub_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ }
+ break;
+ case DOCK_HUB:
+ /* clear Dock dock detected notification */
+ /* Clear accessory detected notification */
+ /* DATA_STATUS_DISABLED_DEVICE_DOCK */
+ update_extcon_dev(pogo_transport, true, true);
+ break;
+ case DOCK_AUDIO_HUB:
+ update_extcon_dev(pogo_transport, true, true);
+ break;
+ case DEVICE_DOCKING_DEBOUNCED:
+ if (docked) {
+ switch_to_hub_locked(pogo_transport);
+ /* switch_to_hub_locked cleared data_active, set it here */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, DOCK_DEVICE_HUB, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, DEVICE_DIRECT, 0);
+ }
+ break;
+ case DOCK_DEVICE_HUB:
+ update_extcon_dev(pogo_transport, true, true);
+ /* DATA_STATUS_DISABLED_DEVICE_DOCK */
+ break;
+ case DEVICE_HUB_DOCKING_DEBOUNCED:
+ if (docked) {
+ pogo_transport_set_state(pogo_transport, DOCK_DEVICE_HUB, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB, 0);
+ }
+ break;
+ case AUDIO_DIRECT_DOCKING_DEBOUNCED:
+ if (docked) {
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT_DOCK_OFFLINE, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT, 0);
+ }
+ break;
+ case AUDIO_DIRECT_DOCK_OFFLINE:
+ /* Push dock detected notification */
+ update_extcon_dev(pogo_transport, true, true);
+ break;
+ case AUDIO_HUB_DOCKING_DEBOUNCED:
+ if (docked) {
+ pogo_transport_set_state(pogo_transport, DOCK_AUDIO_HUB, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB, 0);
+ }
+ break;
+ case HOST_DIRECT:
+ /* DATA_STATUS_ENABLED */
+ /* Clear Pogo accessory Detected */
+ /* Clear USB accessory detected notification */
+ break;
+ case HOST_DIRECT_DOCKING_DEBOUNCED:
+ if (docked) {
+ if (pogo_transport->force_pogo) {
+ switch_to_hub_locked(pogo_transport);
+ /* switch_to_hub_locked cleared data_active, set it here */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, DOCK_HUB_HOST_OFFLINE, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_DOCK_OFFLINE,
+ 0);
+ }
+ } else {
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ }
+ break;
+ case HOST_DIRECT_DOCK_OFFLINE:
+ /* Push Dock dock detected notification */
+ update_extcon_dev(pogo_transport, true, true);
+ break;
+ case DOCK_HUB_HOST_OFFLINE:
+ /* Push accessory detected notification */
+ update_extcon_dev(pogo_transport, true, true);
+ break;
+ case STANDBY_ACC_DEBOUNCED:
+ case DEVICE_DIRECT_ACC_DEBOUNCED:
+ case DEVICE_HUB_ACC_DEBOUNCED:
+ case AUDIO_DIRECT_ACC_DEBOUNCED:
+ case AUDIO_HUB_ACC_DEBOUNCED:
+ case HOST_DIRECT_ACC_DEBOUNCED:
+ /* debounce fail; leave the IRQ and regulator enabled and do nothing */
+ if (!acc_detected)
+ break;
+
+ /*
+ * Disable the IRQ to ignore the noise after POGO Vout is enabled. It will be
+ * re-enabled when HES reports the attach event.
+ */
+ if (pogo_transport->acc_irq_enabled) {
+ disable_irq(pogo_transport->pogo_acc_irq);
+ pogo_transport->acc_irq_enabled = false;
+ }
+
+ /* TODO: queue work for gvotable cast vote if it takes too much time */
+ ret = gvotable_cast_long_vote(pogo_transport->charger_mode_votable, POGO_VOTER,
+ GBMS_POGO_VOUT, 1);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to vote VOUT, ret %d",
+ __func__, ret);
+ break;
+ case ACC_DIRECT:
+ /* Clear Pogo accessory Detected */
+ /* Clear USB accessory detected notification */
+ break;
+ case ACC_DEVICE_HUB:
+ /* DATA_STATUS_DISABLED_DEVICE_DOCK */
+ break;
+ case HOST_DIRECT_ACC_OFFLINE:
+ /* Push Pogo accessory Detected */
+ break;
+ default:
+ break;
+ }
+}
+
+/* Main loop of the State Machine */
+static void pogo_transport_state_machine_work(struct kthread_work *work)
+{
+ struct pogo_transport *pogo_transport =
+ container_of(container_of(work, struct kthread_delayed_work, work),
+ struct pogo_transport, state_machine);
+ struct max77759_plat *chip = pogo_transport->chip;
+ enum pogo_state prev_state;
+
+ mutex_lock(&chip->data_path_lock);
+ pogo_transport->state_machine_running = true;
+
+ if (pogo_transport->delayed_state) {
+ logbuffer_logk(pogo_transport->log, LOGLEVEL_INFO,
+ "state change %s -> %s [delayed %ld ms]",
+ pogo_states[pogo_transport->state],
+ pogo_states[pogo_transport->delayed_state],
+ pogo_transport->delay_ms);
+ pogo_transport->prev_state = pogo_transport->state;
+ pogo_transport->state = pogo_transport->delayed_state;
+ pogo_transport->delayed_state = INVALID_STATE;
+ }
+
+ do {
+ prev_state = pogo_transport->state;
+ pogo_transport_run_state_machine(pogo_transport);
+ } while (pogo_transport->state != prev_state && !pogo_transport->delayed_state);
+
+ pogo_transport->state_machine_running = false;
+ mutex_unlock(&chip->data_path_lock);
+}
+
+/*
+ * Called when POGO Voltage Detection IRQ is active
+ * - Triggered from event: EVENT_POGO_IRQ
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_pogo_irq_active(struct pogo_transport *pogo_transport)
+{
+ switch (pogo_transport->state) {
+ case STANDBY:
+ pogo_transport_set_state(pogo_transport, DOCKING_DEBOUNCED, POGO_PSY_DEBOUNCE_MS);
+ break;
+ case DEVICE_HUB:
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB_DOCKING_DEBOUNCED,
+ POGO_PSY_DEBOUNCE_MS);
+ break;
+ case DEVICE_DIRECT:
+ pogo_transport_set_state(pogo_transport, DEVICE_DOCKING_DEBOUNCED,
+ POGO_PSY_DEBOUNCE_MS);
+ break;
+ case AUDIO_DIRECT:
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT_DOCKING_DEBOUNCED,
+ POGO_PSY_DEBOUNCE_MS);
+ break;
+ case AUDIO_HUB:
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB_DOCKING_DEBOUNCED,
+ POGO_PSY_DEBOUNCE_MS);
+ break;
+ case HOST_DIRECT:
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_DOCKING_DEBOUNCED,
+ POGO_PSY_DEBOUNCE_MS);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when POGO Voltage Detection IRQ is standby
+ * - Triggered from event: EVENT_POGO_IRQ
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_pogo_irq_standby(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ /* Pogo irq in standy implies undocked. Signal userspace before altering data path. */
+ update_extcon_dev(pogo_transport, false, false);
+ switch (pogo_transport->state) {
+ case STANDBY:
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case DOCK_HUB:
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case DOCK_DEVICE_HUB:
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB, 0);
+ break;
+ case DOCK_AUDIO_HUB:
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB, 0);
+ break;
+ case AUDIO_DIRECT_DOCK_OFFLINE:
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT, 0);
+ break;
+ case HOST_DIRECT_DOCK_OFFLINE:
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ case DOCK_HUB_HOST_OFFLINE:
+ /* Clear data_active so that Type-C stack is able to enable the USB data later */
+ chip->data_active = false;
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when USB-C port enters Host Mode
+ * - Triggered from event: EVENT_USBC_DATA_CHANGE
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_usbc_host_on(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ switch (pogo_transport->state) {
+ case STANDBY:
+ pogo_transport_set_state(pogo_transport, DEVICE_DIRECT, 0);
+ break;
+ case DOCK_HUB:
+ /* Set data_active since USB-C device is attached */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, DOCK_DEVICE_HUB, 0);
+ break;
+ case ACC_DIRECT:
+ switch_to_hub_locked(pogo_transport);
+ /* Set data_active since USB-C device is attached */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ case ACC_HUB:
+ /* Set data_active since USB-C device is attached */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when USB-C port leaves Host Mode
+ * - Triggered from event: EVENT_USBC_DATA_CHANGE
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_usbc_host_off(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+ bool ss_attached = pogo_transport->ss_udev_attached;
+
+ pogo_transport->ss_udev_attached = false;
+
+ switch (pogo_transport->state) {
+ case DOCK_DEVICE_HUB:
+ /* Clear data_active since USB-C device is detached */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ break;
+ case DEVICE_HUB:
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case DEVICE_DIRECT:
+ case AUDIO_DIRECT:
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case AUDIO_DIRECT_DOCK_OFFLINE:
+ switch_to_hub_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ break;
+ case DOCK_AUDIO_HUB:
+ /* Clear data_active since USB-C device is detached */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ break;
+ case AUDIO_HUB:
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case ACC_DEVICE_HUB:
+ case ACC_AUDIO_HUB:
+ /* b/271669059 */
+ if (ss_attached) {
+ /* USB_MUX_HUB_SEL set to 0 to bypass the hub */
+ gpio_set_value(pogo_transport->pogo_hub_sel_gpio, 0);
+ logbuffer_log(pogo_transport->log, "POGO: toggling hub-mux, hub-mux:%d",
+ gpio_get_value(pogo_transport->pogo_hub_sel_gpio));
+ mdelay(10);
+ /* USB_MUX_HUB_SEL set to 1 to switch the path to hub */
+ gpio_set_value(pogo_transport->pogo_hub_sel_gpio, 1);
+ logbuffer_log(pogo_transport->log, "POGO: hub-mux:%d",
+ gpio_get_value(pogo_transport->pogo_hub_sel_gpio));
+ }
+
+ /* Clear data_active since USB-C device is detached */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, ACC_HUB, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when USB-C port enters Device Mode
+ * - Triggered from event: EVENT_USBC_DATA_CHANGE
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_usbc_device_on(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ switch (pogo_transport->state) {
+ case STANDBY:
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ case DOCK_HUB:
+ /*
+ * Set data_active so that once USB-C cable is detached later, Type-C stack is able
+ * to call back for the data changed event
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, DOCK_HUB_HOST_OFFLINE, 0);
+ break;
+ case ACC_DIRECT:
+ /*
+ * Set data_active so that once USB-C cable is detached later, Type-C stack is able
+ * to call back for the data changed event
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT_HOST_OFFLINE, 0);
+ break;
+ case ACC_HUB:
+ /*
+ * Set data_active so that once USB-C cable is detached later, Type-C stack is able
+ * to call back for the data changed event
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_HUB_HOST_OFFLINE, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when USB-C port leaves Device Mode
+ * - Triggered from event: EVENT_USBC_DATA_CHANGE
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_usbc_device_off(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ switch (pogo_transport->state) {
+ case HOST_DIRECT:
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case HOST_DIRECT_DOCK_OFFLINE:
+ switch_to_hub_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ break;
+ case DOCK_HUB_HOST_OFFLINE:
+ /*
+ * Clear data_active so that Type-C stack is able to call back for the data changed
+ * event
+ */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, DOCK_HUB, 0);
+ break;
+ case HOST_DIRECT_ACC_OFFLINE:
+ switch_to_pogo_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT, 0);
+ break;
+ case ACC_DIRECT_HOST_OFFLINE:
+ /*
+ * Clear data_active so that Type-C stack is able to call back for the data changed
+ * event
+ */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT, 0);
+ break;
+ case ACC_HUB_HOST_OFFLINE:
+ /*
+ * Clear data_active so that Type-C stack is able to call back for the data changed
+ * event
+ */
+ chip->data_active = false;
+ pogo_transport_set_state(pogo_transport, ACC_HUB, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when device attribute "move_data_to_usb" is written to 1
+ * - Triggered from event: EVENT_ENABLE_USB_DATA
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_enable_usb_data(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ switch (pogo_transport->state) {
+ case DOCK_HUB_HOST_OFFLINE:
+ /*
+ * Clear data_active so that Type-C stack is able to call back for the data changed
+ * event later
+ */
+ chip->data_active = false;
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_DOCK_OFFLINE, 0);
+ break;
+ case ACC_DIRECT_HOST_OFFLINE:
+ case ACC_HUB_HOST_OFFLINE:
+ /*
+ * Clear data_active so that Type-C stack is able to call back for the data changed
+ * event later
+ */
+ chip->data_active = false;
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_ACC_OFFLINE, 0);
+ break;
+ default:
+ return;
+ }
+}
+
+/*
+ * Call this function to:
+ * - Disable POGO OVP
+ * - Disable Accessory Detection IRQ
+ * - Disable POGO Voltage Detection IRQ
+ * - Enable POGO Vout by voting 1 to charger_mode_votable
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_skip_acc_detection(struct pogo_transport *pogo_transport)
+{
+ int ret;
+
+ logbuffer_log(pogo_transport->log, "%s: Skip enabling comparator logic, enable vout",
+ __func__);
+
+ /*
+ * Disable OVP to prevent the voltage going through POGO_VIN. OVP will be re-enabled once
+ * we vote GBMS_POGO_VIN and GBMS gets the votable result.
+ */
+ if (pogo_transport->pogo_ovp_en_gpio >= 0)
+ gpio_set_value_cansleep(pogo_transport->pogo_ovp_en_gpio,
+ !pogo_transport->pogo_ovp_en_active_state);
+
+ if (pogo_transport->acc_irq_enabled) {
+ disable_irq(pogo_transport->pogo_acc_irq);
+ pogo_transport->acc_irq_enabled = false;
+ }
+
+ if (pogo_transport->pogo_irq_enabled) {
+ disable_irq(pogo_transport->pogo_irq);
+ pogo_transport->pogo_irq_enabled = false;
+ }
+
+ ret = gvotable_cast_long_vote(pogo_transport->charger_mode_votable,
+ POGO_VOTER, GBMS_POGO_VOUT, 1);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to vote VOUT, ret %d", __func__,
+ ret);
+}
+
+/*
+ * Called when device attribute "hall1_s" is written to non-zero
+ * - If accessory_detection_enabled == ENABLED, it won't involve the State transition.
+ * Enable the Accessory Detection IRQ and the regulator for the later detection process.
+ * - If accessory_detection_enabled == HALL_ONLY, transition to related ACC states
+ * - Triggered from event: EVENT_HES_H1S_CHANGED
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_hes_acc_detected(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+ int ret;
+
+ if (pogo_transport->accessory_detection_enabled == ENABLED) {
+ switch (pogo_transport->state) {
+ case STANDBY:
+ case DEVICE_HUB:
+ case DEVICE_DIRECT:
+ case AUDIO_DIRECT:
+ case AUDIO_HUB:
+ case HOST_DIRECT:
+ /*
+ * Disable OVP to prevent the voltage going through POGO_VIN. OVP will be
+ * re-enabled once we vote GBMS_POGO_VIN and GBMS gets the votable result.
+ */
+ if (pogo_transport->pogo_ovp_en_gpio >= 0)
+ gpio_set_value_cansleep(pogo_transport->pogo_ovp_en_gpio,
+ !pogo_transport->pogo_ovp_en_active_state);
+
+ if (!pogo_transport->acc_irq_enabled) {
+ enable_irq(pogo_transport->pogo_acc_irq);
+ pogo_transport->acc_irq_enabled = true;
+ }
+
+ ret = pogo_transport_acc_regulator(pogo_transport, true);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to enable acc_detect %d",
+ __func__, ret);
+ break;
+ default:
+ break;
+ }
+ } else if (pogo_transport->accessory_detection_enabled == HALL_ONLY) {
+ switch (pogo_transport->state) {
+ case STANDBY:
+ pogo_transport_skip_acc_detection(pogo_transport);
+ if (!pogo_transport->mfg_acc_test)
+ switch_to_pogo_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT, 0);
+ break;
+ case DEVICE_DIRECT:
+ case AUDIO_DIRECT:
+ pogo_transport_skip_acc_detection(pogo_transport);
+ switch_to_hub_locked(pogo_transport);
+ /*
+ * switch_to_hub_locked cleared data_active. Since there is still a USB-C
+ * accessory attached, set data_active.
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ case DEVICE_HUB:
+ pogo_transport_skip_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ case AUDIO_HUB:
+ pogo_transport_skip_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, ACC_AUDIO_HUB, 0);
+ break;
+ case HOST_DIRECT:
+ pogo_transport_skip_acc_detection(pogo_transport);
+ if (pogo_transport->force_pogo) {
+ switch_to_pogo_locked(pogo_transport);
+ /*
+ * Set data_active so that once USB-C cable is detached later,
+ * Type-C stack is able to call back for the data changed event
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT_HOST_OFFLINE,
+ 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_ACC_OFFLINE,
+ 0);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Called when device attribute "hall1_s" is written to 0
+ * - Triggered from event: EVENT_HES_H1S_CHANGED
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_hes_acc_detached(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+
+ switch (pogo_transport->state) {
+ case STANDBY_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case DEVICE_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DEVICE_DIRECT, 0);
+ break;
+ case DEVICE_HUB_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB, 0);
+ break;
+ case AUDIO_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT, 0);
+ break;
+ case AUDIO_HUB_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB, 0);
+ break;
+ case HOST_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ case ACC_DIRECT:
+ case ACC_HUB:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+ break;
+ case ACC_DEVICE_HUB:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB, 0);
+ break;
+ case ACC_AUDIO_HUB:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB, 0);
+ break;
+ case HOST_DIRECT_ACC_OFFLINE:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ case ACC_DIRECT_HOST_OFFLINE:
+ case ACC_HUB_HOST_OFFLINE:
+ pogo_transport_reset_acc_detection(pogo_transport);
+ /* Clear data_active so that Type-C stack is able to enable the USB data later */
+ chip->data_active = false;
+ switch_to_usbc_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when Accessory Detection IRQ is active
+ * - Triggered from event: EVENT_ACC_GPIO_ACTIVE
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_acc_debouncing(struct pogo_transport *pogo_transport)
+{
+ switch (pogo_transport->state) {
+ case STANDBY:
+ case STANDBY_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, STANDBY_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ case DEVICE_DIRECT:
+ case DEVICE_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, DEVICE_DIRECT_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ case DEVICE_HUB:
+ case DEVICE_HUB_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, DEVICE_HUB_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ case AUDIO_DIRECT:
+ case AUDIO_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ case AUDIO_HUB:
+ case AUDIO_HUB_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, AUDIO_HUB_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ case HOST_DIRECT:
+ case HOST_DIRECT_ACC_DEBOUNCED:
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_ACC_DEBOUNCED,
+ pogo_transport->pogo_acc_gpio_debounce_ms);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when POGO Voltage Detection IRQ is active while Accessory Detection regulator is enabled.
+ * - Triggered from event: EVENT_ACC_CONNECTED
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_acc_connected(struct pogo_transport *pogo_transport)
+{
+ struct max77759_plat *chip = pogo_transport->chip;
+ int ret;
+
+ /*
+ * FIXME: is it possible that when acc regulator is enabled and pogo irq become active
+ * because 12V input through pogo pin? e.g. keep magnet closed to the device and then
+ * docking on korlan?
+ */
+
+ switch (pogo_transport->state) {
+ case STANDBY_ACC_DEBOUNCED:
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d",
+ __func__, ret);
+
+ if (!pogo_transport->mfg_acc_test)
+ switch_to_pogo_locked(pogo_transport);
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT, 0);
+ break;
+ case DEVICE_DIRECT_ACC_DEBOUNCED:
+ case AUDIO_DIRECT_ACC_DEBOUNCED:
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d",
+ __func__, ret);
+
+ switch_to_hub_locked(pogo_transport);
+ /*
+ * switch_to_hub_locked cleared data_active. Since there is still a USB-C accessory
+ * attached, set data_active.
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ case DEVICE_HUB_ACC_DEBOUNCED:
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d",
+ __func__, ret);
+
+ pogo_transport_set_state(pogo_transport, ACC_DEVICE_HUB, 0);
+ break;
+ case AUDIO_HUB_ACC_DEBOUNCED:
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d",
+ __func__, ret);
+
+ pogo_transport_set_state(pogo_transport, ACC_AUDIO_HUB, 0);
+ break;
+ case HOST_DIRECT_ACC_DEBOUNCED:
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ logbuffer_log(pogo_transport->log, "%s: Failed to disable acc_detect %d",
+ __func__, ret);
+
+ if (pogo_transport->force_pogo) {
+ switch_to_pogo_locked(pogo_transport);
+ /*
+ * Set data_active so that once USB-C cable is detached later, Type-C stack
+ * is able to call back for the data changed event
+ */
+ chip->data_active = true;
+ pogo_transport_set_state(pogo_transport, ACC_DIRECT_HOST_OFFLINE, 0);
+ } else {
+ pogo_transport_set_state(pogo_transport, HOST_DIRECT_ACC_OFFLINE, 0);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when a USB device with AUDIO Class is enumerated.
+ * - Triggered from event: EVENT_AUDIO_DEV_ATTACHED
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_audio_dev_attached(struct pogo_transport *pogo_transport)
+{
+ switch (pogo_transport->state) {
+ case DOCK_DEVICE_HUB:
+ pogo_transport_set_state(pogo_transport, DOCK_AUDIO_HUB, 0);
+ break;
+ case DEVICE_DIRECT:
+ pogo_transport_set_state(pogo_transport, AUDIO_DIRECT, 0);
+ break;
+ case ACC_DEVICE_HUB:
+ pogo_transport_set_state(pogo_transport, ACC_AUDIO_HUB, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Called when the detected orientation on USB-C port is changed.
+ * - Triggered from event: EVENT_USBC_ORIENTATION
+ *
+ * This function is guarded by (max77759_plat)->data_path_lock
+ */
+static void pogo_transport_usbc_orientation_changed(struct pogo_transport *pogo_transport)
+{
+ /*
+ * TODO: It is possible that USB-C is toggling between CC2 and Open. We may need to wait for
+ * the orientation being settled and then update the ssphy.
+ */
+ switch (pogo_transport->state) {
+ /* usbc being connected while hub is enabled */
+ case DOCK_HUB:
+ case ACC_HUB:
+ /* usbc being disconnected while hub is enabled */
+ case DOCK_DEVICE_HUB:
+ case DOCK_AUDIO_HUB:
+ case DOCK_HUB_HOST_OFFLINE:
+ case ACC_DEVICE_HUB:
+ case ACC_AUDIO_HUB:
+ pogo_transport_update_polarity(pogo_transport, (int)pogo_transport->polarity, true);
+ ssphy_restart_control(pogo_transport, true);
+ break;
+ default:
+ break;
+ }
+}
+
+static void pogo_transport_event_handler(struct kthread_work *work)
+{
+ struct pogo_transport *pogo_transport = container_of(work, struct pogo_transport,
+ event_work);
+ struct max77759_plat *chip = pogo_transport->chip;
+ unsigned long events;
+
+ mutex_lock(&chip->data_path_lock);
+ spin_lock(&pogo_transport->pogo_event_lock);
+ while (pogo_transport->event_map) {
+ events = pogo_transport->event_map;
+ pogo_transport->event_map = 0;
+
+ spin_unlock(&pogo_transport->pogo_event_lock);
+
+ if (events & EVENT_POGO_IRQ) {
+ int pogo_gpio = gpio_get_value(pogo_transport->pogo_gpio);
+
+ logbuffer_log(pogo_transport->log, "EV:POGO_IRQ %s", pogo_gpio ?
+ "STANDBY" : "ACTIVE");
+ if (pogo_gpio)
+ pogo_transport_pogo_irq_standby(pogo_transport);
+ else
+ pogo_transport_pogo_irq_active(pogo_transport);
+ }
+ if (events & EVENT_USBC_ORIENTATION) {
+ logbuffer_log(pogo_transport->log, "EV:ORIENTATION %u",
+ pogo_transport->polarity);
+ pogo_transport_usbc_orientation_changed(pogo_transport);
+ }
+ if (events & EVENT_USBC_DATA_CHANGE) {
+ logbuffer_log(pogo_transport->log, "EV:DATA_CHANGE usbc-role %u usbc-active %u",
+ pogo_transport->usbc_data_role,
+ pogo_transport->usbc_data_active);
+ if (pogo_transport->usbc_data_role == TYPEC_HOST) {
+ if (pogo_transport->usbc_data_active)
+ pogo_transport_usbc_host_on(pogo_transport);
+ else
+ pogo_transport_usbc_host_off(pogo_transport);
+ } else {
+ if (pogo_transport->usbc_data_active)
+ pogo_transport_usbc_device_on(pogo_transport);
+ else
+ pogo_transport_usbc_device_off(pogo_transport);
+ }
+ }
+ if (events & EVENT_ENABLE_USB_DATA) {
+ logbuffer_log(pogo_transport->log, "EV:ENABLE_USB");
+ pogo_transport_enable_usb_data(pogo_transport);
+ }
+ if (events & EVENT_HES_H1S_CHANGED) {
+ logbuffer_log(pogo_transport->log, "EV:H1S state %d",
+ pogo_transport->hall1_s_state);
+ if (pogo_transport->hall1_s_state)
+ pogo_transport_hes_acc_detected(pogo_transport);
+ else
+ pogo_transport_hes_acc_detached(pogo_transport);
+ }
+ if (events & EVENT_ACC_GPIO_ACTIVE) {
+ logbuffer_log(pogo_transport->log, "EV:ACC_GPIO_ACTIVE");
+ pogo_transport_acc_debouncing(pogo_transport);
+ }
+ if (events & EVENT_ACC_CONNECTED) {
+ logbuffer_log(pogo_transport->log, "EV:ACC_CONNECTED");
+ pogo_transport_acc_connected(pogo_transport);
+ }
+ if (events & EVENT_AUDIO_DEV_ATTACHED) {
+ logbuffer_log(pogo_transport->log, "EV:AUDIO_ATTACHED");
+ pogo_transport_audio_dev_attached(pogo_transport);
+ }
+ spin_lock(&pogo_transport->pogo_event_lock);
+ }
+ spin_unlock(&pogo_transport->pogo_event_lock);
+ mutex_unlock(&chip->data_path_lock);
+}
+
+static void pogo_transport_queue_event(struct pogo_transport *pogo_transport, unsigned long event)
+{
+ unsigned long flags;
+
+ pm_wakeup_event(pogo_transport->dev, POGO_TIMEOUT_MS);
+ /*
+ * Print the event number derived from the bit position; e.g. BIT(0) -> 0
+ * Note that ffs() only return the least significant set bit.
+ */
+ logbuffer_log(pogo_transport->log, "QUEUE EVENT %d", ffs((int)event) - 1);
+
+ spin_lock_irqsave(&pogo_transport->pogo_event_lock, flags);
+ pogo_transport->event_map |= event;
+ spin_unlock_irqrestore(&pogo_transport->pogo_event_lock, flags);
+
+ kthread_queue_work(pogo_transport->wq, &pogo_transport->event_work);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Events triggering */
+/*-------------------------------------------------------------------------*/
+
static irqreturn_t pogo_acc_irq(int irq, void *dev_id)
{
struct pogo_transport *pogo_transport = dev_id;
@@ -756,6 +1979,12 @@ static irqreturn_t pogo_acc_irq(int irq, void *dev_id)
logbuffer_log(pogo_transport->log, "Pogo acc threaded irq running, acc_detect %u",
pogo_acc_gpio);
+ if (pogo_transport->state_machine_enabled) {
+ if (pogo_acc_gpio)
+ pogo_transport_queue_event(pogo_transport, EVENT_ACC_GPIO_ACTIVE);
+ return IRQ_HANDLED;
+ }
+
if (pogo_acc_gpio)
pogo_transport_event(pogo_transport, EVENT_POGO_ACC_DEBOUNCED,
pogo_transport->pogo_acc_gpio_debounce_ms);
@@ -788,7 +2017,10 @@ static irqreturn_t pogo_irq(int irq, void *dev_id)
/* disable the irq to prevent the interrupt storm after pogo 5v out */
disable_irq_nosync(pogo_transport->pogo_irq);
pogo_transport->pogo_irq_enabled = false;
- pogo_transport_event(pogo_transport, EVENT_POGO_ACC_CONNECTED, 0);
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_ACC_CONNECTED);
+ else
+ pogo_transport_event(pogo_transport, EVENT_POGO_ACC_CONNECTED, 0);
}
return IRQ_HANDLED;
}
@@ -808,21 +2040,37 @@ static irqreturn_t pogo_irq(int irq, void *dev_id)
__func__, ret);
}
- /*
- * Signal pogo status change event.
- * Debounce on docking to differentiate between different docks by
- * reading power supply voltage.
- */
- pogo_transport_event(pogo_transport, EVENT_DOCKING, !pogo_gpio ? POGO_PSY_DEBOUNCE_MS : 0);
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_POGO_IRQ);
+ else
+ pogo_transport_event(pogo_transport, EVENT_DOCKING, !pogo_gpio ?
+ POGO_PSY_DEBOUNCE_MS : 0);
return IRQ_HANDLED;
}
-static void data_active_changed(void *data)
+static irqreturn_t pogo_isr(int irq, void *dev_id)
+{
+ struct pogo_transport *pogo_transport = dev_id;
+
+ logbuffer_log(pogo_transport->log, "POGO IRQ triggered");
+ pm_wakeup_event(pogo_transport->dev, POGO_TIMEOUT_MS);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void data_active_changed(void *data, enum typec_data_role role, bool active)
{
struct pogo_transport *pogo_transport = data;
- logbuffer_log(pogo_transport->log, "data active changed");
- pogo_transport_event(pogo_transport, EVENT_DATA_ACTIVE_CHANGED, 0);
+ logbuffer_log(pogo_transport->log, "%s: role %u active %d", __func__, role, active);
+
+ pogo_transport->usbc_data_role = role;
+ pogo_transport->usbc_data_active = active;
+
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_USBC_DATA_CHANGE);
+ else
+ pogo_transport_event(pogo_transport, EVENT_DATA_ACTIVE_CHANGED, 0);
}
static void orientation_changed(void *data)
@@ -832,18 +2080,100 @@ static void orientation_changed(void *data)
if (pogo_transport->polarity != chip->polarity) {
pogo_transport->polarity = chip->polarity;
- pogo_transport_event(pogo_transport, EVENT_ORIENTATION_CHANGED, 0);
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_USBC_ORIENTATION);
+ else
+ pogo_transport_event(pogo_transport, EVENT_ORIENTATION_CHANGED, 0);
}
}
-static irqreturn_t pogo_isr(int irq, void *dev_id)
+static void usb_bus_suspend_resume(void *data, bool main_hcd, bool suspend)
{
- struct pogo_transport *pogo_transport = dev_id;
+ struct pogo_transport *pogo_transport = data;
- logbuffer_log(pogo_transport->log, "POGO IRQ triggered");
- pm_wakeup_event(pogo_transport->dev, POGO_TIMEOUT_MS);
+ if (main_hcd)
+ pogo_transport->main_hcd_suspend = suspend;
+ else
+ pogo_transport->shared_hcd_suspend = suspend;
- return IRQ_WAKE_THREAD;
+ /* TODO: mutex lock to protect the read/set of lid_closed and pending_first_suspend */
+ if (!pogo_transport->lid_closed)
+ return;
+
+ if (pogo_transport->main_hcd_suspend && pogo_transport->shared_hcd_suspend &&
+ pogo_transport->pending_first_suspend) {
+ pogo_transport->pending_first_suspend = false;
+ logbuffer_log(pogo_transport->log, "first bus suspend after lid is closed");
+ }
+}
+
+/* Called when a USB hub/device (exclude root hub) is enumerated */
+static void pogo_transport_udev_add(struct pogo_transport *pogo_transport, struct usb_device *udev)
+{
+ struct usb_interface_descriptor *desc;
+ struct usb_host_config *config;
+ bool audio_dock = false;
+ bool audio_dev = false;
+ int i;
+
+ /* Don't proceed to the event handling if the udev is an Audio Dock. Skip the check. */
+ if (pogo_transport_match_udev(audio_dock_ids, le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct))) {
+ audio_dock = true;
+ goto skip_audio_check;
+ }
+
+ if (udev->speed >= USB_SPEED_SUPER)
+ pogo_transport->ss_udev_attached = true;
+
+ config = udev->config;
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ desc = &config->intf_cache[i]->altsetting->desc;
+ if (desc->bInterfaceClass == USB_CLASS_AUDIO) {
+ audio_dev = true;
+ break;
+ }
+ }
+
+skip_audio_check:
+ logbuffer_log(pogo_transport->log, "udev added %04X:%04X [%s%s%s%s]",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ udev->speed >= USB_SPEED_SUPER ? "Ss" : "",
+ udev->descriptor.bDeviceClass == USB_CLASS_HUB ? "Hu" : "",
+ audio_dock ? "Do" : "",
+ audio_dev ? "Au" : "");
+
+ if (audio_dev && pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_AUDIO_DEV_ATTACHED);
+}
+
+/* notifier callback from usb core */
+static int pogo_transport_udev_notify(struct notifier_block *nb, unsigned long action, void *dev)
+{
+ struct pogo_transport *pogo_transport = container_of(nb, struct pogo_transport, udev_nb);
+ struct usb_device *udev = dev;
+
+ switch (action) {
+ case USB_DEVICE_ADD:
+ /* Don't care about the root hubs. */
+ if (udev->bus->root_hub == udev)
+ break;
+
+ pogo_transport_udev_add(pogo_transport, udev);
+ break;
+ case USB_DEVICE_REMOVE:
+ /* Don't care about the root hubs. */
+ if (udev->bus->root_hub == udev)
+ break;
+
+ logbuffer_log(pogo_transport->log, "udev removed %04X:%04X",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+ break;
+ }
+
+ return NOTIFY_OK;
}
#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -851,6 +2181,11 @@ static int mock_hid_connected_set(void *data, u64 val)
{
struct pogo_transport *pogo_transport = data;
+ if (pogo_transport->state_machine_enabled) {
+ logbuffer_log(pogo_transport->log, "state machine enabled; ignore mock hid");
+ return 0;
+ }
+
pogo_transport->mock_hid_connected = !!val;
logbuffer_log(pogo_transport->log, "%s: %u", __func__, pogo_transport->mock_hid_connected);
@@ -875,6 +2210,10 @@ static int mock_hid_connected_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(mock_hid_connected_fops, mock_hid_connected_get, mock_hid_connected_set,
"%llu\n");
+/*-------------------------------------------------------------------------*/
+/* Initialization */
+/*-------------------------------------------------------------------------*/
+
static void pogo_transport_init_debugfs(struct pogo_transport *pogo_transport)
{
struct dentry *dentry;
@@ -1215,6 +2554,8 @@ static int pogo_transport_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, pogo_transport);
+ spin_lock_init(&pogo_transport->pogo_event_lock);
+
pogo_transport->wq = kthread_create_worker(0, "wq-pogo-transport");
if (IS_ERR_OR_NULL(pogo_transport->wq)) {
ret = PTR_ERR(pogo_transport->wq);
@@ -1223,6 +2564,9 @@ static int pogo_transport_probe(struct platform_device *pdev)
kthread_init_delayed_work(&pogo_transport->pogo_accessory_debounce_work,
process_debounce_event);
+ kthread_init_delayed_work(&pogo_transport->state_machine,
+ pogo_transport_state_machine_work);
+ kthread_init_work(&pogo_transport->event_work, pogo_transport_event_handler);
dn = dev_of_node(pogo_transport->dev);
if (!dn) {
@@ -1293,6 +2637,30 @@ static int pogo_transport_probe(struct platform_device *pdev)
goto psy_put;
}
+ pogo_transport->pending_first_suspend = true;
+
+ /*
+ * modparam_state_machine_enable
+ * 0 or unset: If property "legacy-event-driven" is found in device tree, disable the state
+ * machine. Otherwise, enable/disable the state machine based on
+ * DEFAULT_STATE_MACHINE_ENABLE.
+ * 1: Enable the state machine
+ * 2: Disable the state machine
+ */
+ if (modparam_state_machine_enable == 1) {
+ pogo_transport->state_machine_enabled = true;
+ } else if (modparam_state_machine_enable == 2) {
+ pogo_transport->state_machine_enabled = false;
+ } else {
+ if (of_property_read_bool(pogo_transport->dev->of_node, "legacy-event-driven"))
+ pogo_transport->state_machine_enabled = false;
+ else
+ pogo_transport->state_machine_enabled = DEFAULT_STATE_MACHINE_ENABLE;
+ }
+
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_set_state(pogo_transport, STANDBY, 0);
+
if (modparam_pogo_accessory_enable) {
ret = init_acc_gpio(pogo_transport);
if (ret)
@@ -1324,9 +2692,13 @@ static int pogo_transport_probe(struct platform_device *pdev)
register_data_active_callback(data_active_changed, pogo_transport);
register_orientation_callback(orientation_changed, pogo_transport);
+ register_bus_suspend_callback(usb_bus_suspend_resume, pogo_transport);
+ pogo_transport->udev_nb.notifier_call = pogo_transport_udev_notify;
+ usb_register_notify(&pogo_transport->udev_nb);
/* run once in case orientation has changed before registering the callback */
orientation_changed((void *)pogo_transport);
dev_info(&pdev->dev, "force usb:%d\n", modparam_force_usb ? 1 : 0);
+ dev_info(&pdev->dev, "state machine:%u\n", pogo_transport->state_machine_enabled);
put_device(&data_client->dev);
of_node_put(data_np);
return 0;
@@ -1348,6 +2720,9 @@ static int pogo_transport_remove(struct platform_device *pdev)
{
struct pogo_transport *pogo_transport = platform_get_drvdata(pdev);
struct dentry *dentry;
+ int ret;
+
+ usb_unregister_notify(&pogo_transport->udev_nb);
#if IS_ENABLED(CONFIG_DEBUG_FS)
dentry = debugfs_lookup("pogo_transport", NULL);
@@ -1362,6 +2737,10 @@ static int pogo_transport_remove(struct platform_device *pdev)
if (pogo_transport->hub_ldo && regulator_is_enabled(pogo_transport->hub_ldo) > 0)
regulator_disable(pogo_transport->hub_ldo);
+ ret = pogo_transport_acc_regulator(pogo_transport, false);
+ if (ret)
+ dev_err(pogo_transport->dev, "%s: Failed to disable acc ldo %d\n", __func__, ret);
+
if (pogo_transport->acc_detect_ldo &&
regulator_is_enabled(pogo_transport->acc_detect_ldo) > 0)
regulator_disable(pogo_transport->acc_detect_ldo);
@@ -1379,6 +2758,10 @@ static int pogo_transport_remove(struct platform_device *pdev)
return 0;
}
+/*-------------------------------------------------------------------------*/
+/* Event triggering part.2 */
+/*-------------------------------------------------------------------------*/
+
#define POGO_TRANSPORT_RO_ATTR(_name) \
static ssize_t _name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
@@ -1401,7 +2784,10 @@ static ssize_t move_data_to_usb_store(struct device *dev, struct device_attribut
if (enable != 1)
return -EINVAL;
- pogo_transport_event(pogo_transport, EVENT_MOVE_DATA_TO_USB, 0);
+ if (pogo_transport->state_machine_enabled)
+ pogo_transport_queue_event(pogo_transport, EVENT_ENABLE_USB_DATA);
+ else
+ pogo_transport_event(pogo_transport, EVENT_MOVE_DATA_TO_USB, 0);
return size;
}
@@ -1417,7 +2803,7 @@ static ssize_t force_pogo_store(struct device *dev, struct device_attribute *att
return -EINVAL;
pogo_transport->force_pogo = force_pogo;
- if (force_pogo)
+ if (force_pogo && !pogo_transport->state_machine_enabled)
pogo_transport_event(pogo_transport, EVENT_MOVE_DATA_TO_POGO, 0);
return size;
@@ -1436,6 +2822,11 @@ static ssize_t enable_hub_store(struct device *dev, struct device_attribute *att
struct pogo_transport *pogo_transport = dev_get_drvdata(dev);
u8 enable_hub;
+ if (pogo_transport->state_machine_enabled) {
+ logbuffer_log(pogo_transport->log, "state machine enabled; ignore enable_hub");
+ return size;
+ }
+
if (!pogo_transport->hub_embedded)
return size;
@@ -1481,7 +2872,8 @@ static ssize_t hall1_s_store(struct device *dev, struct device_attribute *attr,
return size;
if (!pogo_transport->accessory_detection_enabled) {
- dev_info(pogo_transport->dev, "Accessory detection disabled\n");
+ logbuffer_logk(pogo_transport->log, LOGLEVEL_INFO, "%s:Accessory detection disabled",
+ __func__);
return size;
}
@@ -1502,8 +2894,14 @@ static ssize_t hall1_s_store(struct device *dev, struct device_attribute *attr,
else
pogo_transport->mfg_acc_test = false;
- dev_info(pogo_transport->dev, "accessory detection %u, mfg %u\n", enable_acc_detect,
- pogo_transport->mfg_acc_test);
+ logbuffer_log(pogo_transport->log, "H1S: accessory detection %u, mfg %u", enable_acc_detect,
+ pogo_transport->mfg_acc_test);
+
+ if (pogo_transport->state_machine_enabled) {
+ pogo_transport_queue_event(pogo_transport, EVENT_HES_H1S_CHANGED);
+ return size;
+ }
+
if (enable_acc_detect)
pogo_transport_event(pogo_transport, EVENT_HALL_SENSOR_ACC_DETECTED, 0);
else
@@ -1516,7 +2914,15 @@ static DEVICE_ATTR_WO(hall1_s);
static ssize_t hall1_n_store(struct device *dev, struct device_attribute *attr, const char *buf,
size_t size)
{
+ struct pogo_transport *pogo_transport = dev_get_drvdata(dev);
+ u8 data;
+
/* Reserved for HES1 Malfunction detection */
+
+ if (kstrtou8(buf, 0, &data))
+ return -EINVAL;
+
+ logbuffer_log(pogo_transport->log, "H1N: %u", data);
return size;
}
static DEVICE_ATTR_WO(hall1_n);
@@ -1524,7 +2930,21 @@ static DEVICE_ATTR_WO(hall1_n);
static ssize_t hall2_s_store(struct device *dev, struct device_attribute *attr, const char *buf,
size_t size)
{
- /* Reserved for keyboard status detection */
+ struct pogo_transport *pogo_transport = dev_get_drvdata(dev);
+ u8 data;
+
+ if (kstrtou8(buf, 0, &data))
+ return -EINVAL;
+
+ if (pogo_transport->lid_closed == !!data)
+ return size;
+
+ pogo_transport->lid_closed = !!data;
+
+ if (!pogo_transport->lid_closed)
+ pogo_transport->pending_first_suspend = true;
+
+ logbuffer_log(pogo_transport->log, "H2S: %u", pogo_transport->lid_closed);
return size;
}
static DEVICE_ATTR_WO(hall2_s);