18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Synaptics NavPoint (PXA27x SSP/SPI) driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/gpio.h>
148c2ecf20Sopenharmony_ci#include <linux/input.h>
158c2ecf20Sopenharmony_ci#include <linux/input/navpoint.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/pxa2xx_ssp.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Synaptics Modular Embedded Protocol: Module Packet Format.
238c2ecf20Sopenharmony_ci * Module header byte 2:0 = Length (# bytes that follow)
248c2ecf20Sopenharmony_ci * Module header byte 4:3 = Control
258c2ecf20Sopenharmony_ci * Module header byte 7:5 = Module Address
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#define HEADER_LENGTH(byte)	((byte) & 0x07)
288c2ecf20Sopenharmony_ci#define HEADER_CONTROL(byte)	(((byte) >> 3) & 0x03)
298c2ecf20Sopenharmony_ci#define HEADER_ADDRESS(byte)	((byte) >> 5)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct navpoint {
328c2ecf20Sopenharmony_ci	struct ssp_device	*ssp;
338c2ecf20Sopenharmony_ci	struct input_dev	*input;
348c2ecf20Sopenharmony_ci	struct device		*dev;
358c2ecf20Sopenharmony_ci	int			gpio;
368c2ecf20Sopenharmony_ci	int			index;
378c2ecf20Sopenharmony_ci	u8			data[1 + HEADER_LENGTH(0xff)];
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Initialization values for SSCR0_x, SSCR1_x, SSSR_x.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistatic const u32 sscr0 = 0
448c2ecf20Sopenharmony_ci	| SSCR0_TUM		/* TIM = 1; No TUR interrupts */
458c2ecf20Sopenharmony_ci	| SSCR0_RIM		/* RIM = 1; No ROR interrupts */
468c2ecf20Sopenharmony_ci	| SSCR0_SSE		/* SSE = 1; SSP enabled */
478c2ecf20Sopenharmony_ci	| SSCR0_Motorola	/* FRF = 0; Motorola SPI */
488c2ecf20Sopenharmony_ci	| SSCR0_DataSize(16)	/* DSS = 15; Data size = 16-bit */
498c2ecf20Sopenharmony_ci	;
508c2ecf20Sopenharmony_cistatic const u32 sscr1 = 0
518c2ecf20Sopenharmony_ci	| SSCR1_SCFR		/* SCFR = 1; SSPSCLK only during transfers */
528c2ecf20Sopenharmony_ci	| SSCR1_SCLKDIR		/* SCLKDIR = 1; Slave mode */
538c2ecf20Sopenharmony_ci	| SSCR1_SFRMDIR		/* SFRMDIR = 1; Slave mode */
548c2ecf20Sopenharmony_ci	| SSCR1_RWOT		/* RWOT = 1; Receive without transmit mode */
558c2ecf20Sopenharmony_ci	| SSCR1_RxTresh(1)	/* RFT = 0; Receive FIFO threshold = 1 */
568c2ecf20Sopenharmony_ci	| SSCR1_SPH		/* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */
578c2ecf20Sopenharmony_ci	| SSCR1_RIE		/* RIE = 1; Receive FIFO interrupt enabled */
588c2ecf20Sopenharmony_ci	;
598c2ecf20Sopenharmony_cistatic const u32 sssr = 0
608c2ecf20Sopenharmony_ci	| SSSR_BCE		/* BCE = 1; Clear BCE */
618c2ecf20Sopenharmony_ci	| SSSR_TUR		/* TUR = 1; Clear TUR */
628c2ecf20Sopenharmony_ci	| SSSR_EOC		/* EOC = 1; Clear EOC */
638c2ecf20Sopenharmony_ci	| SSSR_TINT		/* TINT = 1; Clear TINT */
648c2ecf20Sopenharmony_ci	| SSSR_PINT		/* PINT = 1; Clear PINT */
658c2ecf20Sopenharmony_ci	| SSSR_ROR		/* ROR = 1; Clear ROR */
668c2ecf20Sopenharmony_ci	;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * MEP Query $22: Touchpad Coordinate Range Query is not supported by
708c2ecf20Sopenharmony_ci * the NavPoint module, so sampled values provide the default limits.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_ci#define NAVPOINT_X_MIN		1278
738c2ecf20Sopenharmony_ci#define NAVPOINT_X_MAX		5340
748c2ecf20Sopenharmony_ci#define NAVPOINT_Y_MIN		1572
758c2ecf20Sopenharmony_ci#define NAVPOINT_Y_MAX		4396
768c2ecf20Sopenharmony_ci#define NAVPOINT_PRESSURE_MIN	0
778c2ecf20Sopenharmony_ci#define NAVPOINT_PRESSURE_MAX	255
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void navpoint_packet(struct navpoint *navpoint)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int finger;
828c2ecf20Sopenharmony_ci	int gesture;
838c2ecf20Sopenharmony_ci	int x, y, z;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	switch (navpoint->data[0]) {
868c2ecf20Sopenharmony_ci	case 0xff:	/* Garbage (packet?) between reset and Hello packet */
878c2ecf20Sopenharmony_ci	case 0x00:	/* Module 0, NULL packet */
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	case 0x0e:	/* Module 0, Absolute packet */
918c2ecf20Sopenharmony_ci		finger = (navpoint->data[1] & 0x01);
928c2ecf20Sopenharmony_ci		gesture = (navpoint->data[1] & 0x02);
938c2ecf20Sopenharmony_ci		x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3];
948c2ecf20Sopenharmony_ci		y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5];
958c2ecf20Sopenharmony_ci		z = navpoint->data[6];
968c2ecf20Sopenharmony_ci		input_report_key(navpoint->input, BTN_TOUCH, finger);
978c2ecf20Sopenharmony_ci		input_report_abs(navpoint->input, ABS_X, x);
988c2ecf20Sopenharmony_ci		input_report_abs(navpoint->input, ABS_Y, y);
998c2ecf20Sopenharmony_ci		input_report_abs(navpoint->input, ABS_PRESSURE, z);
1008c2ecf20Sopenharmony_ci		input_report_key(navpoint->input, BTN_TOOL_FINGER, finger);
1018c2ecf20Sopenharmony_ci		input_report_key(navpoint->input, BTN_LEFT, gesture);
1028c2ecf20Sopenharmony_ci		input_sync(navpoint->input);
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	case 0x19:	/* Module 0, Hello packet */
1068c2ecf20Sopenharmony_ci		if ((navpoint->data[1] & 0xf0) == 0x10)
1078c2ecf20Sopenharmony_ci			break;
1088c2ecf20Sopenharmony_ci		fallthrough;
1098c2ecf20Sopenharmony_ci	default:
1108c2ecf20Sopenharmony_ci		dev_warn(navpoint->dev,
1118c2ecf20Sopenharmony_ci			 "spurious packet: data=0x%02x,0x%02x,...\n",
1128c2ecf20Sopenharmony_ci			 navpoint->data[0], navpoint->data[1]);
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic irqreturn_t navpoint_irq(int irq, void *dev_id)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct navpoint *navpoint = dev_id;
1208c2ecf20Sopenharmony_ci	struct ssp_device *ssp = navpoint->ssp;
1218c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
1228c2ecf20Sopenharmony_ci	u32 status;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	status = pxa_ssp_read_reg(ssp, SSSR);
1258c2ecf20Sopenharmony_ci	if (status & sssr) {
1268c2ecf20Sopenharmony_ci		dev_warn(navpoint->dev,
1278c2ecf20Sopenharmony_ci			 "unexpected interrupt: status=0x%08x\n", status);
1288c2ecf20Sopenharmony_ci		pxa_ssp_write_reg(ssp, SSSR, (status & sssr));
1298c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	while (status & SSSR_RNE) {
1338c2ecf20Sopenharmony_ci		u32 data;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		data = pxa_ssp_read_reg(ssp, SSDR);
1368c2ecf20Sopenharmony_ci		navpoint->data[navpoint->index + 0] = (data >> 8);
1378c2ecf20Sopenharmony_ci		navpoint->data[navpoint->index + 1] = data;
1388c2ecf20Sopenharmony_ci		navpoint->index += 2;
1398c2ecf20Sopenharmony_ci		if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) {
1408c2ecf20Sopenharmony_ci			navpoint_packet(navpoint);
1418c2ecf20Sopenharmony_ci			navpoint->index = 0;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		status = pxa_ssp_read_reg(ssp, SSSR);
1448c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void navpoint_up(struct navpoint *navpoint)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct ssp_device *ssp = navpoint->ssp;
1538c2ecf20Sopenharmony_ci	int timeout;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	clk_prepare_enable(ssp->clk);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pxa_ssp_write_reg(ssp, SSCR1, sscr1);
1588c2ecf20Sopenharmony_ci	pxa_ssp_write_reg(ssp, SSSR, sssr);
1598c2ecf20Sopenharmony_ci	pxa_ssp_write_reg(ssp, SSTO, 0);
1608c2ecf20Sopenharmony_ci	pxa_ssp_write_reg(ssp, SSCR0, sscr0);	/* SSCR0_SSE written last */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Wait until SSP port is ready for slave clock operations */
1638c2ecf20Sopenharmony_ci	for (timeout = 100; timeout != 0; --timeout) {
1648c2ecf20Sopenharmony_ci		if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS))
1658c2ecf20Sopenharmony_ci			break;
1668c2ecf20Sopenharmony_ci		msleep(1);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (timeout == 0)
1708c2ecf20Sopenharmony_ci		dev_err(navpoint->dev,
1718c2ecf20Sopenharmony_ci			"timeout waiting for SSSR[CSS] to clear\n");
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (gpio_is_valid(navpoint->gpio))
1748c2ecf20Sopenharmony_ci		gpio_set_value(navpoint->gpio, 1);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void navpoint_down(struct navpoint *navpoint)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct ssp_device *ssp = navpoint->ssp;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (gpio_is_valid(navpoint->gpio))
1828c2ecf20Sopenharmony_ci		gpio_set_value(navpoint->gpio, 0);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	pxa_ssp_write_reg(ssp, SSCR0, 0);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	clk_disable_unprepare(ssp->clk);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int navpoint_open(struct input_dev *input)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct navpoint *navpoint = input_get_drvdata(input);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	navpoint_up(navpoint);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void navpoint_close(struct input_dev *input)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct navpoint *navpoint = input_get_drvdata(input);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	navpoint_down(navpoint);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int navpoint_probe(struct platform_device *pdev)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	const struct navpoint_platform_data *pdata =
2088c2ecf20Sopenharmony_ci					dev_get_platdata(&pdev->dev);
2098c2ecf20Sopenharmony_ci	struct ssp_device *ssp;
2108c2ecf20Sopenharmony_ci	struct input_dev *input;
2118c2ecf20Sopenharmony_ci	struct navpoint *navpoint;
2128c2ecf20Sopenharmony_ci	int error;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (!pdata) {
2158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no platform data\n");
2168c2ecf20Sopenharmony_ci		return -EINVAL;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->gpio)) {
2208c2ecf20Sopenharmony_ci		error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW,
2218c2ecf20Sopenharmony_ci					 "SYNAPTICS_ON");
2228c2ecf20Sopenharmony_ci		if (error)
2238c2ecf20Sopenharmony_ci			return error;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ssp = pxa_ssp_request(pdata->port, pdev->name);
2278c2ecf20Sopenharmony_ci	if (!ssp) {
2288c2ecf20Sopenharmony_ci		error = -ENODEV;
2298c2ecf20Sopenharmony_ci		goto err_free_gpio;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* HaRET does not disable devices before jumping into Linux */
2338c2ecf20Sopenharmony_ci	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
2348c2ecf20Sopenharmony_ci		pxa_ssp_write_reg(ssp, SSCR0, 0);
2358c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL);
2398c2ecf20Sopenharmony_ci	input = input_allocate_device();
2408c2ecf20Sopenharmony_ci	if (!navpoint || !input) {
2418c2ecf20Sopenharmony_ci		error = -ENOMEM;
2428c2ecf20Sopenharmony_ci		goto err_free_mem;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	navpoint->ssp = ssp;
2468c2ecf20Sopenharmony_ci	navpoint->input = input;
2478c2ecf20Sopenharmony_ci	navpoint->dev = &pdev->dev;
2488c2ecf20Sopenharmony_ci	navpoint->gpio = pdata->gpio;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	input->name = pdev->name;
2518c2ecf20Sopenharmony_ci	input->dev.parent = &pdev->dev;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input->evbit);
2548c2ecf20Sopenharmony_ci	__set_bit(EV_ABS, input->evbit);
2558c2ecf20Sopenharmony_ci	__set_bit(BTN_LEFT, input->keybit);
2568c2ecf20Sopenharmony_ci	__set_bit(BTN_TOUCH, input->keybit);
2578c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_FINGER, input->keybit);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_X,
2608c2ecf20Sopenharmony_ci			     NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0);
2618c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_Y,
2628c2ecf20Sopenharmony_ci			     NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0);
2638c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_PRESSURE,
2648c2ecf20Sopenharmony_ci			     NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX,
2658c2ecf20Sopenharmony_ci			     0, 0);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	input->open = navpoint_open;
2688c2ecf20Sopenharmony_ci	input->close = navpoint_close;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	input_set_drvdata(input, navpoint);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint);
2738c2ecf20Sopenharmony_ci	if (error)
2748c2ecf20Sopenharmony_ci		goto err_free_mem;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	error = input_register_device(input);
2778c2ecf20Sopenharmony_ci	if (error)
2788c2ecf20Sopenharmony_ci		goto err_free_irq;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, navpoint);
2818c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return 0;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cierr_free_irq:
2868c2ecf20Sopenharmony_ci	free_irq(ssp->irq, navpoint);
2878c2ecf20Sopenharmony_cierr_free_mem:
2888c2ecf20Sopenharmony_ci	input_free_device(input);
2898c2ecf20Sopenharmony_ci	kfree(navpoint);
2908c2ecf20Sopenharmony_ci	pxa_ssp_free(ssp);
2918c2ecf20Sopenharmony_cierr_free_gpio:
2928c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->gpio))
2938c2ecf20Sopenharmony_ci		gpio_free(pdata->gpio);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return error;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int navpoint_remove(struct platform_device *pdev)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	const struct navpoint_platform_data *pdata =
3018c2ecf20Sopenharmony_ci					dev_get_platdata(&pdev->dev);
3028c2ecf20Sopenharmony_ci	struct navpoint *navpoint = platform_get_drvdata(pdev);
3038c2ecf20Sopenharmony_ci	struct ssp_device *ssp = navpoint->ssp;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	free_irq(ssp->irq, navpoint);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	input_unregister_device(navpoint->input);
3088c2ecf20Sopenharmony_ci	kfree(navpoint);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	pxa_ssp_free(ssp);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->gpio))
3138c2ecf20Sopenharmony_ci		gpio_free(pdata->gpio);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int __maybe_unused navpoint_suspend(struct device *dev)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
3218c2ecf20Sopenharmony_ci	struct navpoint *navpoint = platform_get_drvdata(pdev);
3228c2ecf20Sopenharmony_ci	struct input_dev *input = navpoint->input;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	mutex_lock(&input->mutex);
3258c2ecf20Sopenharmony_ci	if (input->users)
3268c2ecf20Sopenharmony_ci		navpoint_down(navpoint);
3278c2ecf20Sopenharmony_ci	mutex_unlock(&input->mutex);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int __maybe_unused navpoint_resume(struct device *dev)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
3358c2ecf20Sopenharmony_ci	struct navpoint *navpoint = platform_get_drvdata(pdev);
3368c2ecf20Sopenharmony_ci	struct input_dev *input = navpoint->input;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	mutex_lock(&input->mutex);
3398c2ecf20Sopenharmony_ci	if (input->users)
3408c2ecf20Sopenharmony_ci		navpoint_up(navpoint);
3418c2ecf20Sopenharmony_ci	mutex_unlock(&input->mutex);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic struct platform_driver navpoint_driver = {
3498c2ecf20Sopenharmony_ci	.probe		= navpoint_probe,
3508c2ecf20Sopenharmony_ci	.remove		= navpoint_remove,
3518c2ecf20Sopenharmony_ci	.driver = {
3528c2ecf20Sopenharmony_ci		.name	= "navpoint",
3538c2ecf20Sopenharmony_ci		.pm	= &navpoint_pm_ops,
3548c2ecf20Sopenharmony_ci	},
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cimodule_platform_driver(navpoint_driver);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
3608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver");
3618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3628c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:navpoint");
363