18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SiRFstar GNSS receiver driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/gnss.h>
108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/pm.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/serdev.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/wait.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SIRF_BOOT_DELAY			500
258c2ecf20Sopenharmony_ci#define SIRF_ON_OFF_PULSE_TIME		100
268c2ecf20Sopenharmony_ci#define SIRF_ACTIVATE_TIMEOUT		200
278c2ecf20Sopenharmony_ci#define SIRF_HIBERNATE_TIMEOUT		200
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * If no data arrives for this time, we assume that the chip is off.
308c2ecf20Sopenharmony_ci * REVISIT: The report cycle is configurable and can be several minutes long,
318c2ecf20Sopenharmony_ci * so this will only work reliably if the report cycle is set to a reasonable
328c2ecf20Sopenharmony_ci * low value. Also power saving settings (like send data only on movement)
338c2ecf20Sopenharmony_ci * might things work even worse.
348c2ecf20Sopenharmony_ci * Workaround might be to parse shutdown or bootup messages.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci#define SIRF_REPORT_CYCLE	2000
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct sirf_data {
398c2ecf20Sopenharmony_ci	struct gnss_device *gdev;
408c2ecf20Sopenharmony_ci	struct serdev_device *serdev;
418c2ecf20Sopenharmony_ci	speed_t	speed;
428c2ecf20Sopenharmony_ci	struct regulator *vcc;
438c2ecf20Sopenharmony_ci	struct regulator *lna;
448c2ecf20Sopenharmony_ci	struct gpio_desc *on_off;
458c2ecf20Sopenharmony_ci	struct gpio_desc *wakeup;
468c2ecf20Sopenharmony_ci	int irq;
478c2ecf20Sopenharmony_ci	bool active;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	struct mutex gdev_mutex;
508c2ecf20Sopenharmony_ci	bool open;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	struct mutex serdev_mutex;
538c2ecf20Sopenharmony_ci	int serdev_count;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	wait_queue_head_t power_wait;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int sirf_serdev_open(struct sirf_data *data)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	int ret = 0;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	mutex_lock(&data->serdev_mutex);
638c2ecf20Sopenharmony_ci	if (++data->serdev_count == 1) {
648c2ecf20Sopenharmony_ci		ret = serdev_device_open(data->serdev);
658c2ecf20Sopenharmony_ci		if (ret) {
668c2ecf20Sopenharmony_ci			data->serdev_count--;
678c2ecf20Sopenharmony_ci			goto out_unlock;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		serdev_device_set_baudrate(data->serdev, data->speed);
718c2ecf20Sopenharmony_ci		serdev_device_set_flow_control(data->serdev, false);
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciout_unlock:
758c2ecf20Sopenharmony_ci	mutex_unlock(&data->serdev_mutex);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return ret;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void sirf_serdev_close(struct sirf_data *data)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	mutex_lock(&data->serdev_mutex);
838c2ecf20Sopenharmony_ci	if (--data->serdev_count == 0)
848c2ecf20Sopenharmony_ci		serdev_device_close(data->serdev);
858c2ecf20Sopenharmony_ci	mutex_unlock(&data->serdev_mutex);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int sirf_open(struct gnss_device *gdev)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct sirf_data *data = gnss_get_drvdata(gdev);
918c2ecf20Sopenharmony_ci	struct serdev_device *serdev = data->serdev;
928c2ecf20Sopenharmony_ci	int ret;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	mutex_lock(&data->gdev_mutex);
958c2ecf20Sopenharmony_ci	data->open = true;
968c2ecf20Sopenharmony_ci	mutex_unlock(&data->gdev_mutex);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ret = sirf_serdev_open(data);
998c2ecf20Sopenharmony_ci	if (ret) {
1008c2ecf20Sopenharmony_ci		mutex_lock(&data->gdev_mutex);
1018c2ecf20Sopenharmony_ci		data->open = false;
1028c2ecf20Sopenharmony_ci		mutex_unlock(&data->gdev_mutex);
1038c2ecf20Sopenharmony_ci		return ret;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(&serdev->dev);
1078c2ecf20Sopenharmony_ci	if (ret < 0) {
1088c2ecf20Sopenharmony_ci		dev_err(&gdev->dev, "failed to runtime resume: %d\n", ret);
1098c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(&serdev->dev);
1108c2ecf20Sopenharmony_ci		goto err_close;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cierr_close:
1168c2ecf20Sopenharmony_ci	sirf_serdev_close(data);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	mutex_lock(&data->gdev_mutex);
1198c2ecf20Sopenharmony_ci	data->open = false;
1208c2ecf20Sopenharmony_ci	mutex_unlock(&data->gdev_mutex);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return ret;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void sirf_close(struct gnss_device *gdev)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct sirf_data *data = gnss_get_drvdata(gdev);
1288c2ecf20Sopenharmony_ci	struct serdev_device *serdev = data->serdev;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	sirf_serdev_close(data);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	pm_runtime_put(&serdev->dev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mutex_lock(&data->gdev_mutex);
1358c2ecf20Sopenharmony_ci	data->open = false;
1368c2ecf20Sopenharmony_ci	mutex_unlock(&data->gdev_mutex);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf,
1408c2ecf20Sopenharmony_ci				size_t count)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct sirf_data *data = gnss_get_drvdata(gdev);
1438c2ecf20Sopenharmony_ci	struct serdev_device *serdev = data->serdev;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* write is only buffered synchronously */
1478c2ecf20Sopenharmony_ci	ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
1488c2ecf20Sopenharmony_ci	if (ret < 0 || ret < count)
1498c2ecf20Sopenharmony_ci		return ret;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* FIXME: determine if interrupted? */
1528c2ecf20Sopenharmony_ci	serdev_device_wait_until_sent(serdev, 0);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return count;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic const struct gnss_operations sirf_gnss_ops = {
1588c2ecf20Sopenharmony_ci	.open		= sirf_open,
1598c2ecf20Sopenharmony_ci	.close		= sirf_close,
1608c2ecf20Sopenharmony_ci	.write_raw	= sirf_write_raw,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int sirf_receive_buf(struct serdev_device *serdev,
1648c2ecf20Sopenharmony_ci				const unsigned char *buf, size_t count)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct sirf_data *data = serdev_device_get_drvdata(serdev);
1678c2ecf20Sopenharmony_ci	struct gnss_device *gdev = data->gdev;
1688c2ecf20Sopenharmony_ci	int ret = 0;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (!data->wakeup && !data->active) {
1718c2ecf20Sopenharmony_ci		data->active = true;
1728c2ecf20Sopenharmony_ci		wake_up_interruptible(&data->power_wait);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	mutex_lock(&data->gdev_mutex);
1768c2ecf20Sopenharmony_ci	if (data->open)
1778c2ecf20Sopenharmony_ci		ret = gnss_insert_raw(gdev, buf, count);
1788c2ecf20Sopenharmony_ci	mutex_unlock(&data->gdev_mutex);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const struct serdev_device_ops sirf_serdev_ops = {
1848c2ecf20Sopenharmony_ci	.receive_buf	= sirf_receive_buf,
1858c2ecf20Sopenharmony_ci	.write_wakeup	= serdev_device_write_wakeup,
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic irqreturn_t sirf_wakeup_handler(int irq, void *dev_id)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct sirf_data *data = dev_id;
1918c2ecf20Sopenharmony_ci	struct device *dev = &data->serdev->dev;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = gpiod_get_value_cansleep(data->wakeup);
1958c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - wakeup = %d\n", __func__, ret);
1968c2ecf20Sopenharmony_ci	if (ret < 0)
1978c2ecf20Sopenharmony_ci		goto out;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	data->active = ret;
2008c2ecf20Sopenharmony_ci	wake_up_interruptible(&data->power_wait);
2018c2ecf20Sopenharmony_ciout:
2028c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int sirf_wait_for_power_state_nowakeup(struct sirf_data *data,
2068c2ecf20Sopenharmony_ci						bool active,
2078c2ecf20Sopenharmony_ci						unsigned long timeout)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int ret;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* Wait for state change (including any shutdown messages). */
2128c2ecf20Sopenharmony_ci	msleep(timeout);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Wait for data reception or timeout. */
2158c2ecf20Sopenharmony_ci	data->active = false;
2168c2ecf20Sopenharmony_ci	ret = wait_event_interruptible_timeout(data->power_wait,
2178c2ecf20Sopenharmony_ci			data->active, msecs_to_jiffies(SIRF_REPORT_CYCLE));
2188c2ecf20Sopenharmony_ci	if (ret < 0)
2198c2ecf20Sopenharmony_ci		return ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (ret > 0 && !active)
2228c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (ret == 0 && active)
2258c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int sirf_wait_for_power_state(struct sirf_data *data, bool active,
2318c2ecf20Sopenharmony_ci					unsigned long timeout)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int ret;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!data->wakeup)
2368c2ecf20Sopenharmony_ci		return sirf_wait_for_power_state_nowakeup(data, active, timeout);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ret = wait_event_interruptible_timeout(data->power_wait,
2398c2ecf20Sopenharmony_ci			data->active == active, msecs_to_jiffies(timeout));
2408c2ecf20Sopenharmony_ci	if (ret < 0)
2418c2ecf20Sopenharmony_ci		return ret;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (ret == 0) {
2448c2ecf20Sopenharmony_ci		dev_warn(&data->serdev->dev, "timeout waiting for active state = %d\n",
2458c2ecf20Sopenharmony_ci				active);
2468c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void sirf_pulse_on_off(struct sirf_data *data)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(data->on_off, 1);
2558c2ecf20Sopenharmony_ci	msleep(SIRF_ON_OFF_PULSE_TIME);
2568c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(data->on_off, 0);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int sirf_set_active(struct sirf_data *data, bool active)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	unsigned long timeout;
2628c2ecf20Sopenharmony_ci	int retries = 3;
2638c2ecf20Sopenharmony_ci	int ret;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (active)
2668c2ecf20Sopenharmony_ci		timeout = SIRF_ACTIVATE_TIMEOUT;
2678c2ecf20Sopenharmony_ci	else
2688c2ecf20Sopenharmony_ci		timeout = SIRF_HIBERNATE_TIMEOUT;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (!data->wakeup) {
2718c2ecf20Sopenharmony_ci		ret = sirf_serdev_open(data);
2728c2ecf20Sopenharmony_ci		if (ret)
2738c2ecf20Sopenharmony_ci			return ret;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	do {
2778c2ecf20Sopenharmony_ci		sirf_pulse_on_off(data);
2788c2ecf20Sopenharmony_ci		ret = sirf_wait_for_power_state(data, active, timeout);
2798c2ecf20Sopenharmony_ci	} while (ret == -ETIMEDOUT && retries--);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!data->wakeup)
2828c2ecf20Sopenharmony_ci		sirf_serdev_close(data);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (ret)
2858c2ecf20Sopenharmony_ci		return ret;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int sirf_runtime_suspend(struct device *dev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct sirf_data *data = dev_get_drvdata(dev);
2938c2ecf20Sopenharmony_ci	int ret2;
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (data->on_off)
2978c2ecf20Sopenharmony_ci		ret = sirf_set_active(data, false);
2988c2ecf20Sopenharmony_ci	else
2998c2ecf20Sopenharmony_ci		ret = regulator_disable(data->vcc);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (ret)
3028c2ecf20Sopenharmony_ci		return ret;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	ret = regulator_disable(data->lna);
3058c2ecf20Sopenharmony_ci	if (ret)
3068c2ecf20Sopenharmony_ci		goto err_reenable;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cierr_reenable:
3118c2ecf20Sopenharmony_ci	if (data->on_off)
3128c2ecf20Sopenharmony_ci		ret2 = sirf_set_active(data, true);
3138c2ecf20Sopenharmony_ci	else
3148c2ecf20Sopenharmony_ci		ret2 = regulator_enable(data->vcc);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (ret2)
3178c2ecf20Sopenharmony_ci		dev_err(dev,
3188c2ecf20Sopenharmony_ci			"failed to reenable power on failed suspend: %d\n",
3198c2ecf20Sopenharmony_ci			ret2);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return ret;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int sirf_runtime_resume(struct device *dev)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct sirf_data *data = dev_get_drvdata(dev);
3278c2ecf20Sopenharmony_ci	int ret;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = regulator_enable(data->lna);
3308c2ecf20Sopenharmony_ci	if (ret)
3318c2ecf20Sopenharmony_ci		return ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (data->on_off)
3348c2ecf20Sopenharmony_ci		ret = sirf_set_active(data, true);
3358c2ecf20Sopenharmony_ci	else
3368c2ecf20Sopenharmony_ci		ret = regulator_enable(data->vcc);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (ret)
3398c2ecf20Sopenharmony_ci		goto err_disable_lna;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cierr_disable_lna:
3448c2ecf20Sopenharmony_ci	regulator_disable(data->lna);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return ret;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int __maybe_unused sirf_suspend(struct device *dev)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct sirf_data *data = dev_get_drvdata(dev);
3528c2ecf20Sopenharmony_ci	int ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dev))
3558c2ecf20Sopenharmony_ci		ret = sirf_runtime_suspend(dev);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (data->wakeup)
3588c2ecf20Sopenharmony_ci		disable_irq(data->irq);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int __maybe_unused sirf_resume(struct device *dev)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct sirf_data *data = dev_get_drvdata(dev);
3668c2ecf20Sopenharmony_ci	int ret = 0;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (data->wakeup)
3698c2ecf20Sopenharmony_ci		enable_irq(data->irq);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dev))
3728c2ecf20Sopenharmony_ci		ret = sirf_runtime_resume(dev);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return ret;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sirf_pm_ops = {
3788c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(sirf_suspend, sirf_resume)
3798c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(sirf_runtime_suspend, sirf_runtime_resume, NULL)
3808c2ecf20Sopenharmony_ci};
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int sirf_parse_dt(struct serdev_device *serdev)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct sirf_data *data = serdev_device_get_drvdata(serdev);
3858c2ecf20Sopenharmony_ci	struct device_node *node = serdev->dev.of_node;
3868c2ecf20Sopenharmony_ci	u32 speed = 9600;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	of_property_read_u32(node, "current-speed", &speed);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	data->speed = speed;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return 0;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int sirf_probe(struct serdev_device *serdev)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct device *dev = &serdev->dev;
3988c2ecf20Sopenharmony_ci	struct gnss_device *gdev;
3998c2ecf20Sopenharmony_ci	struct sirf_data *data;
4008c2ecf20Sopenharmony_ci	int ret;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
4038c2ecf20Sopenharmony_ci	if (!data)
4048c2ecf20Sopenharmony_ci		return -ENOMEM;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	gdev = gnss_allocate_device(dev);
4078c2ecf20Sopenharmony_ci	if (!gdev)
4088c2ecf20Sopenharmony_ci		return -ENOMEM;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	gdev->type = GNSS_TYPE_SIRF;
4118c2ecf20Sopenharmony_ci	gdev->ops = &sirf_gnss_ops;
4128c2ecf20Sopenharmony_ci	gnss_set_drvdata(gdev, data);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	data->serdev = serdev;
4158c2ecf20Sopenharmony_ci	data->gdev = gdev;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	mutex_init(&data->gdev_mutex);
4188c2ecf20Sopenharmony_ci	mutex_init(&data->serdev_mutex);
4198c2ecf20Sopenharmony_ci	init_waitqueue_head(&data->power_wait);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	serdev_device_set_drvdata(serdev, data);
4228c2ecf20Sopenharmony_ci	serdev_device_set_client_ops(serdev, &sirf_serdev_ops);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	ret = sirf_parse_dt(serdev);
4258c2ecf20Sopenharmony_ci	if (ret)
4268c2ecf20Sopenharmony_ci		goto err_put_device;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	data->vcc = devm_regulator_get(dev, "vcc");
4298c2ecf20Sopenharmony_ci	if (IS_ERR(data->vcc)) {
4308c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->vcc);
4318c2ecf20Sopenharmony_ci		goto err_put_device;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	data->lna = devm_regulator_get(dev, "lna");
4358c2ecf20Sopenharmony_ci	if (IS_ERR(data->lna)) {
4368c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->lna);
4378c2ecf20Sopenharmony_ci		goto err_put_device;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	data->on_off = devm_gpiod_get_optional(dev, "sirf,onoff",
4418c2ecf20Sopenharmony_ci			GPIOD_OUT_LOW);
4428c2ecf20Sopenharmony_ci	if (IS_ERR(data->on_off)) {
4438c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->on_off);
4448c2ecf20Sopenharmony_ci		goto err_put_device;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (data->on_off) {
4488c2ecf20Sopenharmony_ci		data->wakeup = devm_gpiod_get_optional(dev, "sirf,wakeup",
4498c2ecf20Sopenharmony_ci				GPIOD_IN);
4508c2ecf20Sopenharmony_ci		if (IS_ERR(data->wakeup)) {
4518c2ecf20Sopenharmony_ci			ret = PTR_ERR(data->wakeup);
4528c2ecf20Sopenharmony_ci			goto err_put_device;
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		ret = regulator_enable(data->vcc);
4568c2ecf20Sopenharmony_ci		if (ret)
4578c2ecf20Sopenharmony_ci			goto err_put_device;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		/* Wait for chip to boot into hibernate mode. */
4608c2ecf20Sopenharmony_ci		msleep(SIRF_BOOT_DELAY);
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (data->wakeup) {
4648c2ecf20Sopenharmony_ci		ret = gpiod_get_value_cansleep(data->wakeup);
4658c2ecf20Sopenharmony_ci		if (ret < 0)
4668c2ecf20Sopenharmony_ci			goto err_disable_vcc;
4678c2ecf20Sopenharmony_ci		data->active = ret;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		ret = gpiod_to_irq(data->wakeup);
4708c2ecf20Sopenharmony_ci		if (ret < 0)
4718c2ecf20Sopenharmony_ci			goto err_disable_vcc;
4728c2ecf20Sopenharmony_ci		data->irq = ret;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		ret = request_threaded_irq(data->irq, NULL, sirf_wakeup_handler,
4758c2ecf20Sopenharmony_ci				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
4768c2ecf20Sopenharmony_ci				"wakeup", data);
4778c2ecf20Sopenharmony_ci		if (ret)
4788c2ecf20Sopenharmony_ci			goto err_disable_vcc;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (data->on_off) {
4828c2ecf20Sopenharmony_ci		if (!data->wakeup) {
4838c2ecf20Sopenharmony_ci			data->active = false;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci			ret = sirf_serdev_open(data);
4868c2ecf20Sopenharmony_ci			if (ret)
4878c2ecf20Sopenharmony_ci				goto err_disable_vcc;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci			msleep(SIRF_REPORT_CYCLE);
4908c2ecf20Sopenharmony_ci			sirf_serdev_close(data);
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		/* Force hibernate mode if already active. */
4948c2ecf20Sopenharmony_ci		if (data->active) {
4958c2ecf20Sopenharmony_ci			ret = sirf_set_active(data, false);
4968c2ecf20Sopenharmony_ci			if (ret) {
4978c2ecf20Sopenharmony_ci				dev_err(dev, "failed to set hibernate mode: %d\n",
4988c2ecf20Sopenharmony_ci						ret);
4998c2ecf20Sopenharmony_ci				goto err_free_irq;
5008c2ecf20Sopenharmony_ci			}
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM)) {
5058c2ecf20Sopenharmony_ci		pm_runtime_set_suspended(dev);	/* clear runtime_error flag */
5068c2ecf20Sopenharmony_ci		pm_runtime_enable(dev);
5078c2ecf20Sopenharmony_ci	} else {
5088c2ecf20Sopenharmony_ci		ret = sirf_runtime_resume(dev);
5098c2ecf20Sopenharmony_ci		if (ret < 0)
5108c2ecf20Sopenharmony_ci			goto err_free_irq;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	ret = gnss_register_device(gdev);
5148c2ecf20Sopenharmony_ci	if (ret)
5158c2ecf20Sopenharmony_ci		goto err_disable_rpm;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cierr_disable_rpm:
5208c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM))
5218c2ecf20Sopenharmony_ci		pm_runtime_disable(dev);
5228c2ecf20Sopenharmony_ci	else
5238c2ecf20Sopenharmony_ci		sirf_runtime_suspend(dev);
5248c2ecf20Sopenharmony_cierr_free_irq:
5258c2ecf20Sopenharmony_ci	if (data->wakeup)
5268c2ecf20Sopenharmony_ci		free_irq(data->irq, data);
5278c2ecf20Sopenharmony_cierr_disable_vcc:
5288c2ecf20Sopenharmony_ci	if (data->on_off)
5298c2ecf20Sopenharmony_ci		regulator_disable(data->vcc);
5308c2ecf20Sopenharmony_cierr_put_device:
5318c2ecf20Sopenharmony_ci	gnss_put_device(data->gdev);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return ret;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void sirf_remove(struct serdev_device *serdev)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct sirf_data *data = serdev_device_get_drvdata(serdev);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	gnss_deregister_device(data->gdev);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM))
5438c2ecf20Sopenharmony_ci		pm_runtime_disable(&serdev->dev);
5448c2ecf20Sopenharmony_ci	else
5458c2ecf20Sopenharmony_ci		sirf_runtime_suspend(&serdev->dev);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (data->wakeup)
5488c2ecf20Sopenharmony_ci		free_irq(data->irq, data);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (data->on_off)
5518c2ecf20Sopenharmony_ci		regulator_disable(data->vcc);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	gnss_put_device(data->gdev);
5548c2ecf20Sopenharmony_ci};
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
5578c2ecf20Sopenharmony_cistatic const struct of_device_id sirf_of_match[] = {
5588c2ecf20Sopenharmony_ci	{ .compatible = "fastrax,uc430" },
5598c2ecf20Sopenharmony_ci	{ .compatible = "linx,r4" },
5608c2ecf20Sopenharmony_ci	{ .compatible = "wi2wi,w2sg0004" },
5618c2ecf20Sopenharmony_ci	{ .compatible = "wi2wi,w2sg0008i" },
5628c2ecf20Sopenharmony_ci	{ .compatible = "wi2wi,w2sg0084i" },
5638c2ecf20Sopenharmony_ci	{},
5648c2ecf20Sopenharmony_ci};
5658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sirf_of_match);
5668c2ecf20Sopenharmony_ci#endif
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic struct serdev_device_driver sirf_driver = {
5698c2ecf20Sopenharmony_ci	.driver	= {
5708c2ecf20Sopenharmony_ci		.name		= "gnss-sirf",
5718c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(sirf_of_match),
5728c2ecf20Sopenharmony_ci		.pm		= &sirf_pm_ops,
5738c2ecf20Sopenharmony_ci	},
5748c2ecf20Sopenharmony_ci	.probe	= sirf_probe,
5758c2ecf20Sopenharmony_ci	.remove	= sirf_remove,
5768c2ecf20Sopenharmony_ci};
5778c2ecf20Sopenharmony_cimodule_serdev_device_driver(sirf_driver);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
5808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SiRFstar GNSS receiver driver");
5818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
582