enum svc_wakedetect_state wake_detect_state;
int wake_detect_irq;
- spinlock_t wake_lock; /* Protect wake_detect_state */
+ spinlock_t wake_lock; /* Protect wake_detect_state */
+ struct mutex platform_state_mutex; /* Protect state */
unsigned long wake_detect_start;
struct notifier_block pm_notifier;
struct device *dev;
};
+/*
+ * arche_platform_change_state: Change the operational state
+ *
+ * This exported function allows external drivers to change the state
+ * of the arche-platform driver.
+ * Note that this function only supports transitions between two states
+ * with limited functionality.
+ *
+ * - ARCHE_PLATFORM_STATE_TIME_SYNC:
+ * Once set, allows timesync operations between SVC <=> AP and makes
+ * sure that arche-platform driver ignores any subsequent events/pulses
+ * from SVC over wake/detect.
+ *
+ * - ARCHE_PLATFORM_STATE_ACTIVE:
+ * Puts back driver to active state, where any pulse from SVC on wake/detect
+ * line would trigger either cold/standby boot.
+ * Note: Transition request from this function does not trigger cold/standby
+ * boot. It just puts back driver book keeping variable back to ACTIVE
+ * state and restores the interrupt.
+ *
+ * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
+ * satisfy the requested state-transition or -EINVAL for all other
+ * state-transition requests.
+ */
+int arche_platform_change_state(enum arche_platform_state state)
+{
+ struct arche_platform_drvdata *arche_pdata;
+ struct platform_device *pdev;
+ struct device_node *np;
+ int ret = -EAGAIN;
+ unsigned long flags;
+
+ np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
+ if (!np) {
+ pr_err("google,arche-platform device node not found\n");
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ pr_err("arche-platform device not found\n");
+ return -ENODEV;
+ }
+
+ arche_pdata = platform_get_drvdata(pdev);
+
+ mutex_lock(&arche_pdata->platform_state_mutex);
+ spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+ if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
+ dev_err(arche_pdata->dev,
+ "driver busy with wake/detect line ops\n");
+ goto exit;
+ }
+
+ if (arche_pdata->state == state) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF ||
+ arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY ||
+ arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) {
+ dev_err(arche_pdata->dev, "busy, request to retry later\n");
+ goto exit;
+ }
+
+ if (state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
+ disable_irq(arche_pdata->wake_detect_irq);
+ arche_pdata->state = ARCHE_PLATFORM_STATE_TIME_SYNC;
+ } else if (state == ARCHE_PLATFORM_STATE_ACTIVE) {
+ arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
+ enable_irq(arche_pdata->wake_detect_irq);
+ } else {
+ dev_err(arche_pdata->dev, "invalid state transition request\n");
+ }
+exit:
+ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+ mutex_unlock(&arche_pdata->platform_state_mutex);
+ of_node_put(np);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arche_platform_change_state);
+
static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
{
gpio_set_value(gpio, onoff);
return IRQ_HANDLED;
}
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
{
int ret;
return 0;
}
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
{
if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
}
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
{
unsigned long flags;
struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
int ret = 0;
+ mutex_lock(&arche_pdata->platform_state_mutex);
+
if (sysfs_streq(buf, "off")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
- return count;
+ goto exit;
/* If SVC goes down, bring down APB's as well */
device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
} else if (sysfs_streq(buf, "active")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
- return count;
+ goto exit;
ret = arche_platform_coldboot_seq(arche_pdata);
assert_wakedetect(arche_pdata);
} else if (sysfs_streq(buf, "standby")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
- return count;
+ goto exit;
dev_warn(arche_pdata->dev, "standby state not supported\n");
} else if (sysfs_streq(buf, "fw_flashing")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
- return count;
+ goto exit;
/* First we want to make sure we power off everything
* and then enter FW flashing state */
ret = -EINVAL;
}
+exit:
+ mutex_unlock(&arche_pdata->platform_state_mutex);
return ret ? ret : count;
}
return sprintf(buf, "standby\n");
case ARCHE_PLATFORM_STATE_FW_FLASHING:
return sprintf(buf, "fw_flashing\n");
+ case ARCHE_PLATFORM_STATE_TIME_SYNC:
+ return sprintf(buf, "time_sync\n");
default:
return sprintf(buf, "unknown state\n");
}
struct arche_platform_drvdata *arche_pdata =
container_of(notifier, struct arche_platform_drvdata,
pm_notifier);
+ int ret = NOTIFY_DONE;
+ mutex_lock(&arche_pdata->platform_state_mutex);
switch (pm_event) {
case PM_SUSPEND_PREPARE:
- if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE)
- return NOTIFY_STOP;
+ if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
+ ret = NOTIFY_STOP;
+ break;
+ }
device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
arche_platform_poweroff_seq(arche_pdata);
break;
default:
break;
}
+ mutex_unlock(&arche_pdata->platform_state_mutex);
- return NOTIFY_DONE;
+ return ret;
}
static int arche_platform_probe(struct platform_device *pdev)
arche_pdata->dev = &pdev->dev;
spin_lock_init(&arche_pdata->wake_lock);
+ mutex_init(&arche_pdata->platform_state_mutex);
arche_pdata->wake_detect_irq =
gpio_to_irq(arche_pdata->wake_detect_gpio);
return ret;
}
+ mutex_lock(&arche_pdata->platform_state_mutex);
ret = arche_platform_coldboot_seq(arche_pdata);
if (ret) {
dev_err(dev, "Failed to cold boot svc %d\n", ret);
arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
ret = register_pm_notifier(&arche_pdata->pm_notifier);
+ mutex_unlock(&arche_pdata->platform_state_mutex);
+
if (ret) {
dev_err(dev, "failed to register pm notifier %d\n", ret);
goto err_populate;
err_populate:
arche_platform_poweroff_seq(arche_pdata);
err_coldboot:
+ mutex_unlock(&arche_pdata->platform_state_mutex);
device_remove_file(&pdev->dev, &dev_attr_state);
return ret;
}