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