| 
									
										
										
										
											2012-05-10 22:11:51 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Synaptics NavPoint (PXA27x SSP/SPI) driver. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/clk.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/gpio.h>
 | 
					
						
							|  |  |  | #include <linux/input.h>
 | 
					
						
							|  |  |  | #include <linux/input/navpoint.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/mutex.h>
 | 
					
						
							|  |  |  | #include <linux/pxa2xx_ssp.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Synaptics Modular Embedded Protocol: Module Packet Format. | 
					
						
							|  |  |  |  * Module header byte 2:0 = Length (# bytes that follow) | 
					
						
							|  |  |  |  * Module header byte 4:3 = Control | 
					
						
							|  |  |  |  * Module header byte 7:5 = Module Address | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define HEADER_LENGTH(byte)	((byte) & 0x07)
 | 
					
						
							|  |  |  | #define HEADER_CONTROL(byte)	(((byte) >> 3) & 0x03)
 | 
					
						
							|  |  |  | #define HEADER_ADDRESS(byte)	((byte) >> 5)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct navpoint { | 
					
						
							|  |  |  | 	struct ssp_device	*ssp; | 
					
						
							|  |  |  | 	struct input_dev	*input; | 
					
						
							|  |  |  | 	struct device		*dev; | 
					
						
							|  |  |  | 	int			gpio; | 
					
						
							|  |  |  | 	int			index; | 
					
						
							|  |  |  | 	u8			data[1 + HEADER_LENGTH(0xff)]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static const u32 sscr0 = 0 | 
					
						
							|  |  |  | 	| SSCR0_TUM		/* TIM = 1; No TUR interrupts */ | 
					
						
							|  |  |  | 	| SSCR0_RIM		/* RIM = 1; No ROR interrupts */ | 
					
						
							|  |  |  | 	| SSCR0_SSE		/* SSE = 1; SSP enabled */ | 
					
						
							|  |  |  | 	| SSCR0_Motorola	/* FRF = 0; Motorola SPI */ | 
					
						
							|  |  |  | 	| SSCR0_DataSize(16)	/* DSS = 15; Data size = 16-bit */ | 
					
						
							|  |  |  | 	; | 
					
						
							|  |  |  | static const u32 sscr1 = 0 | 
					
						
							|  |  |  | 	| SSCR1_SCFR		/* SCFR = 1; SSPSCLK only during transfers */ | 
					
						
							|  |  |  | 	| SSCR1_SCLKDIR		/* SCLKDIR = 1; Slave mode */ | 
					
						
							|  |  |  | 	| SSCR1_SFRMDIR		/* SFRMDIR = 1; Slave mode */ | 
					
						
							|  |  |  | 	| SSCR1_RWOT		/* RWOT = 1; Receive without transmit mode */ | 
					
						
							|  |  |  | 	| SSCR1_RxTresh(1)	/* RFT = 0; Receive FIFO threshold = 1 */ | 
					
						
							|  |  |  | 	| SSCR1_SPH		/* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ | 
					
						
							|  |  |  | 	| SSCR1_RIE		/* RIE = 1; Receive FIFO interrupt enabled */ | 
					
						
							|  |  |  | 	; | 
					
						
							|  |  |  | static const u32 sssr = 0 | 
					
						
							|  |  |  | 	| SSSR_BCE		/* BCE = 1; Clear BCE */ | 
					
						
							|  |  |  | 	| SSSR_TUR		/* TUR = 1; Clear TUR */ | 
					
						
							|  |  |  | 	| SSSR_EOC		/* EOC = 1; Clear EOC */ | 
					
						
							|  |  |  | 	| SSSR_TINT		/* TINT = 1; Clear TINT */ | 
					
						
							|  |  |  | 	| SSSR_PINT		/* PINT = 1; Clear PINT */ | 
					
						
							|  |  |  | 	| SSSR_ROR		/* ROR = 1; Clear ROR */ | 
					
						
							|  |  |  | 	; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * MEP Query $22: Touchpad Coordinate Range Query is not supported by | 
					
						
							|  |  |  |  * the NavPoint module, so sampled values provide the default limits. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define NAVPOINT_X_MIN		1278
 | 
					
						
							|  |  |  | #define NAVPOINT_X_MAX		5340
 | 
					
						
							|  |  |  | #define NAVPOINT_Y_MIN		1572
 | 
					
						
							|  |  |  | #define NAVPOINT_Y_MAX		4396
 | 
					
						
							|  |  |  | #define NAVPOINT_PRESSURE_MIN	0
 | 
					
						
							|  |  |  | #define NAVPOINT_PRESSURE_MAX	255
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void navpoint_packet(struct navpoint *navpoint) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int finger; | 
					
						
							|  |  |  | 	int gesture; | 
					
						
							|  |  |  | 	int x, y, z; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (navpoint->data[0]) { | 
					
						
							|  |  |  | 	case 0xff:	/* Garbage (packet?) between reset and Hello packet */ | 
					
						
							|  |  |  | 	case 0x00:	/* Module 0, NULL packet */ | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0x0e:	/* Module 0, Absolute packet */ | 
					
						
							|  |  |  | 		finger = (navpoint->data[1] & 0x01); | 
					
						
							|  |  |  | 		gesture = (navpoint->data[1] & 0x02); | 
					
						
							|  |  |  | 		x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; | 
					
						
							|  |  |  | 		y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; | 
					
						
							|  |  |  | 		z = navpoint->data[6]; | 
					
						
							|  |  |  | 		input_report_key(navpoint->input, BTN_TOUCH, finger); | 
					
						
							|  |  |  | 		input_report_abs(navpoint->input, ABS_X, x); | 
					
						
							|  |  |  | 		input_report_abs(navpoint->input, ABS_Y, y); | 
					
						
							|  |  |  | 		input_report_abs(navpoint->input, ABS_PRESSURE, z); | 
					
						
							|  |  |  | 		input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); | 
					
						
							|  |  |  | 		input_report_key(navpoint->input, BTN_LEFT, gesture); | 
					
						
							|  |  |  | 		input_sync(navpoint->input); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0x19:	/* Module 0, Hello packet */ | 
					
						
							|  |  |  | 		if ((navpoint->data[1] & 0xf0) == 0x10) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		dev_warn(navpoint->dev, | 
					
						
							|  |  |  | 			 "spurious packet: data=0x%02x,0x%02x,...\n", | 
					
						
							|  |  |  | 			 navpoint->data[0], navpoint->data[1]); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static irqreturn_t navpoint_irq(int irq, void *dev_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct navpoint *navpoint = dev_id; | 
					
						
							|  |  |  | 	struct ssp_device *ssp = navpoint->ssp; | 
					
						
							|  |  |  | 	irqreturn_t ret = IRQ_NONE; | 
					
						
							|  |  |  | 	u32 status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = pxa_ssp_read_reg(ssp, SSSR); | 
					
						
							|  |  |  | 	if (status & sssr) { | 
					
						
							|  |  |  | 		dev_warn(navpoint->dev, | 
					
						
							|  |  |  | 			 "unexpected interrupt: status=0x%08x\n", status); | 
					
						
							|  |  |  | 		pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); | 
					
						
							|  |  |  | 		ret = IRQ_HANDLED; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (status & SSSR_RNE) { | 
					
						
							|  |  |  | 		u32 data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		data = pxa_ssp_read_reg(ssp, SSDR); | 
					
						
							|  |  |  | 		navpoint->data[navpoint->index + 0] = (data >> 8); | 
					
						
							|  |  |  | 		navpoint->data[navpoint->index + 1] = data; | 
					
						
							|  |  |  | 		navpoint->index += 2; | 
					
						
							|  |  |  | 		if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { | 
					
						
							|  |  |  | 			navpoint_packet(navpoint); | 
					
						
							|  |  |  | 			navpoint->index = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		status = pxa_ssp_read_reg(ssp, SSSR); | 
					
						
							|  |  |  | 		ret = IRQ_HANDLED; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void navpoint_up(struct navpoint *navpoint) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ssp_device *ssp = navpoint->ssp; | 
					
						
							|  |  |  | 	int timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_prepare_enable(ssp->clk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pxa_ssp_write_reg(ssp, SSCR1, sscr1); | 
					
						
							|  |  |  | 	pxa_ssp_write_reg(ssp, SSSR, sssr); | 
					
						
							|  |  |  | 	pxa_ssp_write_reg(ssp, SSTO, 0); | 
					
						
							|  |  |  | 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);	/* SSCR0_SSE written last */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Wait until SSP port is ready for slave clock operations */ | 
					
						
							|  |  |  | 	for (timeout = 100; timeout != 0; --timeout) { | 
					
						
							|  |  |  | 		if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		msleep(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (timeout == 0) | 
					
						
							|  |  |  | 		dev_err(navpoint->dev, | 
					
						
							|  |  |  | 			"timeout waiting for SSSR[CSS] to clear\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gpio_is_valid(navpoint->gpio)) | 
					
						
							|  |  |  | 		gpio_set_value(navpoint->gpio, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void navpoint_down(struct navpoint *navpoint) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ssp_device *ssp = navpoint->ssp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gpio_is_valid(navpoint->gpio)) | 
					
						
							|  |  |  | 		gpio_set_value(navpoint->gpio, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pxa_ssp_write_reg(ssp, SSCR0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_disable_unprepare(ssp->clk); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int navpoint_open(struct input_dev *input) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct navpoint *navpoint = input_get_drvdata(input); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	navpoint_up(navpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void navpoint_close(struct input_dev *input) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct navpoint *navpoint = input_get_drvdata(input); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	navpoint_down(navpoint); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 21:38:25 -08:00
										 |  |  | static int navpoint_probe(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2012-05-10 22:11:51 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct navpoint_platform_data *pdata = | 
					
						
							|  |  |  | 					dev_get_platdata(&pdev->dev); | 
					
						
							|  |  |  | 	struct ssp_device *ssp; | 
					
						
							|  |  |  | 	struct input_dev *input; | 
					
						
							|  |  |  | 	struct navpoint *navpoint; | 
					
						
							|  |  |  | 	int error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!pdata) { | 
					
						
							|  |  |  | 		dev_err(&pdev->dev, "no platform data\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gpio_is_valid(pdata->gpio)) { | 
					
						
							|  |  |  | 		error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, | 
					
						
							|  |  |  | 					 "SYNAPTICS_ON"); | 
					
						
							|  |  |  | 		if (error) | 
					
						
							|  |  |  | 			return error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ssp = pxa_ssp_request(pdata->port, pdev->name); | 
					
						
							|  |  |  | 	if (!ssp) { | 
					
						
							|  |  |  | 		error = -ENODEV; | 
					
						
							|  |  |  | 		goto err_free_gpio; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* HaRET does not disable devices before jumping into Linux */ | 
					
						
							|  |  |  | 	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { | 
					
						
							|  |  |  | 		pxa_ssp_write_reg(ssp, SSCR0, 0); | 
					
						
							|  |  |  | 		dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); | 
					
						
							|  |  |  | 	input = input_allocate_device(); | 
					
						
							|  |  |  | 	if (!navpoint || !input) { | 
					
						
							|  |  |  | 		error = -ENOMEM; | 
					
						
							|  |  |  | 		goto err_free_mem; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	navpoint->ssp = ssp; | 
					
						
							|  |  |  | 	navpoint->input = input; | 
					
						
							|  |  |  | 	navpoint->dev = &pdev->dev; | 
					
						
							|  |  |  | 	navpoint->gpio = pdata->gpio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input->name = pdev->name; | 
					
						
							|  |  |  | 	input->dev.parent = &pdev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__set_bit(EV_KEY, input->evbit); | 
					
						
							|  |  |  | 	__set_bit(EV_ABS, input->evbit); | 
					
						
							|  |  |  | 	__set_bit(BTN_LEFT, input->keybit); | 
					
						
							|  |  |  | 	__set_bit(BTN_TOUCH, input->keybit); | 
					
						
							|  |  |  | 	__set_bit(BTN_TOOL_FINGER, input->keybit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input_set_abs_params(input, ABS_X, | 
					
						
							|  |  |  | 			     NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); | 
					
						
							|  |  |  | 	input_set_abs_params(input, ABS_Y, | 
					
						
							|  |  |  | 			     NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); | 
					
						
							|  |  |  | 	input_set_abs_params(input, ABS_PRESSURE, | 
					
						
							|  |  |  | 			     NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, | 
					
						
							|  |  |  | 			     0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input->open = navpoint_open; | 
					
						
							|  |  |  | 	input->close = navpoint_close; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input_set_drvdata(input, navpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); | 
					
						
							|  |  |  | 	if (error) | 
					
						
							|  |  |  | 		goto err_free_mem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = input_register_device(input); | 
					
						
							|  |  |  | 	if (error) | 
					
						
							|  |  |  | 		goto err_free_irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, navpoint); | 
					
						
							|  |  |  | 	dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err_free_irq: | 
					
						
							| 
									
										
										
										
											2013-05-23 09:30:23 -07:00
										 |  |  | 	free_irq(ssp->irq, navpoint); | 
					
						
							| 
									
										
										
										
											2012-05-10 22:11:51 -07:00
										 |  |  | err_free_mem: | 
					
						
							|  |  |  | 	input_free_device(input); | 
					
						
							|  |  |  | 	kfree(navpoint); | 
					
						
							|  |  |  | 	pxa_ssp_free(ssp); | 
					
						
							|  |  |  | err_free_gpio: | 
					
						
							|  |  |  | 	if (gpio_is_valid(pdata->gpio)) | 
					
						
							|  |  |  | 		gpio_free(pdata->gpio); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 21:50:47 -08:00
										 |  |  | static int navpoint_remove(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2012-05-10 22:11:51 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct navpoint_platform_data *pdata = | 
					
						
							|  |  |  | 					dev_get_platdata(&pdev->dev); | 
					
						
							|  |  |  | 	struct navpoint *navpoint = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 	struct ssp_device *ssp = navpoint->ssp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	free_irq(ssp->irq, navpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input_unregister_device(navpoint->input); | 
					
						
							|  |  |  | 	kfree(navpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pxa_ssp_free(ssp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gpio_is_valid(pdata->gpio)) | 
					
						
							|  |  |  | 		gpio_free(pdata->gpio); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM_SLEEP
 | 
					
						
							|  |  |  | static int navpoint_suspend(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct platform_device *pdev = to_platform_device(dev); | 
					
						
							|  |  |  | 	struct navpoint *navpoint = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 	struct input_dev *input = navpoint->input; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&input->mutex); | 
					
						
							|  |  |  | 	if (input->users) | 
					
						
							|  |  |  | 		navpoint_down(navpoint); | 
					
						
							|  |  |  | 	mutex_unlock(&input->mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int navpoint_resume(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct platform_device *pdev = to_platform_device(dev); | 
					
						
							|  |  |  | 	struct navpoint *navpoint = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 	struct input_dev *input = navpoint->input; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&input->mutex); | 
					
						
							|  |  |  | 	if (input->users) | 
					
						
							|  |  |  | 		navpoint_up(navpoint); | 
					
						
							|  |  |  | 	mutex_unlock(&input->mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver navpoint_driver = { | 
					
						
							|  |  |  | 	.probe		= navpoint_probe, | 
					
						
							| 
									
										
										
										
											2012-11-23 21:27:39 -08:00
										 |  |  | 	.remove		= navpoint_remove, | 
					
						
							| 
									
										
										
										
											2012-05-10 22:11:51 -07:00
										 |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.name	= "navpoint", | 
					
						
							|  |  |  | 		.owner	= THIS_MODULE, | 
					
						
							|  |  |  | 		.pm	= &navpoint_pm_ops, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_platform_driver(navpoint_driver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_ALIAS("platform:navpoint"); |