| @@ -0,0 +1,369 @@ |
| | 1 | /* |
| | 2 | * Synaptics NavPoint (PXA27x SSP/SPI) driver. |
| | 3 | * |
| | 4 | * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com> |
| | 5 | * |
| | 6 | * This program is free software; you can redistribute it and/or modify |
| | 7 | * it under the terms of the GNU General Public License version 2 as |
| | 8 | * published by the Free Software Foundation. |
| | 9 | */ |
| | 10 | |
| | 11 | #include <linux/kernel.h> |
| | 12 | #include <linux/init.h> |
| | 13 | #include <linux/module.h> |
| | 14 | #include <linux/platform_device.h> |
| | 15 | #include <linux/clk.h> |
| | 16 | #include <linux/delay.h> |
| | 17 | #include <linux/gpio.h> |
| | 18 | #include <linux/input.h> |
| | 19 | #include <linux/input/navpoint.h> |
| | 20 | #include <linux/interrupt.h> |
| | 21 | #include <linux/mutex.h> |
| | 22 | #include <linux/pxa2xx_ssp.h> |
| | 23 | #include <linux/slab.h> |
| | 24 | |
| | 25 | /* |
| | 26 | * Synaptics Modular Embedded Protocol: Module Packet Format. |
| | 27 | * Module header byte 2:0 = Length (# bytes that follow) |
| | 28 | * Module header byte 4:3 = Control |
| | 29 | * Module header byte 7:5 = Module Address |
| | 30 | */ |
| | 31 | #define HEADER_LENGTH(byte) ((byte) & 0x07) |
| | 32 | #define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03) |
| | 33 | #define HEADER_ADDRESS(byte) ((byte) >> 5) |
| | 34 | |
| | 35 | struct navpoint { |
| | 36 | struct ssp_device *ssp; |
| | 37 | struct input_dev *input; |
| | 38 | struct device *dev; |
| | 39 | int gpio; |
| | 40 | int index; |
| | 41 | u8 data[1 + HEADER_LENGTH(0xff)]; |
| | 42 | }; |
| | 43 | |
| | 44 | /* |
| | 45 | * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. |
| | 46 | */ |
| | 47 | static const u32 sscr0 = 0 |
| | 48 | | SSCR0_TUM /* TIM = 1; No TUR interrupts */ |
| | 49 | | SSCR0_RIM /* RIM = 1; No ROR interrupts */ |
| | 50 | | SSCR0_SSE /* SSE = 1; SSP enabled */ |
| | 51 | | SSCR0_Motorola /* FRF = 0; Motorola SPI */ |
| | 52 | | SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */ |
| | 53 | ; |
| | 54 | static const u32 sscr1 = 0 |
| | 55 | | SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */ |
| | 56 | | SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */ |
| | 57 | | SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */ |
| | 58 | | SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */ |
| | 59 | | SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */ |
| | 60 | | SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ |
| | 61 | | SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */ |
| | 62 | ; |
| | 63 | static const u32 sssr = 0 |
| | 64 | | SSSR_BCE /* BCE = 1; Clear BCE */ |
| | 65 | | SSSR_TUR /* TUR = 1; Clear TUR */ |
| | 66 | | SSSR_EOC /* EOC = 1; Clear EOC */ |
| | 67 | | SSSR_TINT /* TINT = 1; Clear TINT */ |
| | 68 | | SSSR_PINT /* PINT = 1; Clear PINT */ |
| | 69 | | SSSR_ROR /* ROR = 1; Clear ROR */ |
| | 70 | ; |
| | 71 | |
| | 72 | /* |
| | 73 | * MEP Query $22: Touchpad Coordinate Range Query is not supported by |
| | 74 | * the NavPoint module, so sampled values provide the default limits. |
| | 75 | */ |
| | 76 | #define NAVPOINT_X_MIN 1278 |
| | 77 | #define NAVPOINT_X_MAX 5340 |
| | 78 | #define NAVPOINT_Y_MIN 1572 |
| | 79 | #define NAVPOINT_Y_MAX 4396 |
| | 80 | #define NAVPOINT_PRESSURE_MIN 0 |
| | 81 | #define NAVPOINT_PRESSURE_MAX 255 |
| | 82 | |
| | 83 | static void navpoint_packet(struct navpoint *navpoint) |
| | 84 | { |
| | 85 | int finger; |
| | 86 | int gesture; |
| | 87 | int x, y, z; |
| | 88 | |
| | 89 | switch (navpoint->data[0]) { |
| | 90 | case 0xff: /* Garbage (packet?) between reset and Hello packet */ |
| | 91 | case 0x00: /* Module 0, NULL packet */ |
| | 92 | break; |
| | 93 | |
| | 94 | case 0x0e: /* Module 0, Absolute packet */ |
| | 95 | finger = (navpoint->data[1] & 0x01); |
| | 96 | gesture = (navpoint->data[1] & 0x02); |
| | 97 | x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; |
| | 98 | y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; |
| | 99 | z = navpoint->data[6]; |
| | 100 | input_report_key(navpoint->input, BTN_TOUCH, finger); |
| | 101 | input_report_abs(navpoint->input, ABS_X, x); |
| | 102 | input_report_abs(navpoint->input, ABS_Y, y); |
| | 103 | input_report_abs(navpoint->input, ABS_PRESSURE, z); |
| | 104 | input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); |
| | 105 | input_report_key(navpoint->input, BTN_LEFT, gesture); |
| | 106 | input_sync(navpoint->input); |
| | 107 | break; |
| | 108 | |
| | 109 | case 0x19: /* Module 0, Hello packet */ |
| | 110 | if ((navpoint->data[1] & 0xf0) == 0x10) |
| | 111 | break; |
| | 112 | /* FALLTHROUGH */ |
| | 113 | default: |
| | 114 | dev_warn(navpoint->dev, |
| | 115 | "spurious packet: data=0x%02x,0x%02x,...\n", |
| | 116 | navpoint->data[0], navpoint->data[1]); |
| | 117 | break; |
| | 118 | } |
| | 119 | } |
| | 120 | |
| | 121 | static irqreturn_t navpoint_irq(int irq, void *dev_id) |
| | 122 | { |
| | 123 | struct navpoint *navpoint = dev_id; |
| | 124 | struct ssp_device *ssp = navpoint->ssp; |
| | 125 | irqreturn_t ret = IRQ_NONE; |
| | 126 | u32 status; |
| | 127 | |
| | 128 | status = pxa_ssp_read_reg(ssp, SSSR); |
| | 129 | if (status & sssr) { |
| | 130 | dev_warn(navpoint->dev, |
| | 131 | "unexpected interrupt: status=0x%08x\n", status); |
| | 132 | pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); |
| | 133 | ret = IRQ_HANDLED; |
| | 134 | } |
| | 135 | |
| | 136 | while (status & SSSR_RNE) { |
| | 137 | u32 data; |
| | 138 | |
| | 139 | data = pxa_ssp_read_reg(ssp, SSDR); |
| | 140 | navpoint->data[navpoint->index + 0] = (data >> 8); |
| | 141 | navpoint->data[navpoint->index + 1] = data; |
| | 142 | navpoint->index += 2; |
| | 143 | if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { |
| | 144 | navpoint_packet(navpoint); |
| | 145 | navpoint->index = 0; |
| | 146 | } |
| | 147 | status = pxa_ssp_read_reg(ssp, SSSR); |
| | 148 | ret = IRQ_HANDLED; |
| | 149 | } |
| | 150 | |
| | 151 | return ret; |
| | 152 | } |
| | 153 | |
| | 154 | static void navpoint_up(struct navpoint *navpoint) |
| | 155 | { |
| | 156 | struct ssp_device *ssp = navpoint->ssp; |
| | 157 | int timeout; |
| | 158 | |
| | 159 | clk_prepare_enable(ssp->clk); |
| | 160 | |
| | 161 | pxa_ssp_write_reg(ssp, SSCR1, sscr1); |
| | 162 | pxa_ssp_write_reg(ssp, SSSR, sssr); |
| | 163 | pxa_ssp_write_reg(ssp, SSTO, 0); |
| | 164 | pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */ |
| | 165 | |
| | 166 | /* Wait until SSP port is ready for slave clock operations */ |
| | 167 | for (timeout = 100; timeout != 0; --timeout) { |
| | 168 | if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) |
| | 169 | break; |
| | 170 | msleep(1); |
| | 171 | } |
| | 172 | |
| | 173 | if (timeout == 0) |
| | 174 | dev_err(navpoint->dev, |
| | 175 | "timeout waiting for SSSR[CSS] to clear\n"); |
| | 176 | |
| | 177 | if (gpio_is_valid(navpoint->gpio)) |
| | 178 | gpio_set_value(navpoint->gpio, 1); |
| | 179 | } |
| | 180 | |
| | 181 | static void navpoint_down(struct navpoint *navpoint) |
| | 182 | { |
| | 183 | struct ssp_device *ssp = navpoint->ssp; |
| | 184 | |
| | 185 | if (gpio_is_valid(navpoint->gpio)) |
| | 186 | gpio_set_value(navpoint->gpio, 0); |
| | 187 | |
| | 188 | pxa_ssp_write_reg(ssp, SSCR0, 0); |
| | 189 | |
| | 190 | clk_disable_unprepare(ssp->clk); |
| | 191 | } |
| | 192 | |
| | 193 | static int navpoint_open(struct input_dev *input) |
| | 194 | { |
| | 195 | struct navpoint *navpoint = input_get_drvdata(input); |
| | 196 | |
| | 197 | navpoint_up(navpoint); |
| | 198 | |
| | 199 | return 0; |
| | 200 | } |
| | 201 | |
| | 202 | static void navpoint_close(struct input_dev *input) |
| | 203 | { |
| | 204 | struct navpoint *navpoint = input_get_drvdata(input); |
| | 205 | |
| | 206 | navpoint_down(navpoint); |
| | 207 | } |
| | 208 | |
| | 209 | static int __devinit navpoint_probe(struct platform_device *pdev) |
| | 210 | { |
| | 211 | const struct navpoint_platform_data *pdata = |
| | 212 | dev_get_platdata(&pdev->dev); |
| | 213 | struct ssp_device *ssp; |
| | 214 | struct input_dev *input; |
| | 215 | struct navpoint *navpoint; |
| | 216 | int error; |
| | 217 | |
| | 218 | if (!pdata) { |
| | 219 | dev_err(&pdev->dev, "no platform data\n"); |
| | 220 | return -EINVAL; |
| | 221 | } |
| | 222 | |
| | 223 | if (gpio_is_valid(pdata->gpio)) { |
| | 224 | error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, |
| | 225 | "SYNAPTICS_ON"); |
| | 226 | if (error) |
| | 227 | return error; |
| | 228 | } |
| | 229 | |
| | 230 | ssp = pxa_ssp_request(pdata->port, pdev->name); |
| | 231 | if (!ssp) { |
| | 232 | error = -ENODEV; |
| | 233 | goto err_free_gpio; |
| | 234 | } |
| | 235 | |
| | 236 | /* HaRET does not disable devices before jumping into Linux */ |
| | 237 | if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { |
| | 238 | pxa_ssp_write_reg(ssp, SSCR0, 0); |
| | 239 | dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); |
| | 240 | } |
| | 241 | |
| | 242 | navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); |
| | 243 | input = input_allocate_device(); |
| | 244 | if (!navpoint || !input) { |
| | 245 | error = -ENOMEM; |
| | 246 | goto err_free_mem; |
| | 247 | } |
| | 248 | |
| | 249 | navpoint->ssp = ssp; |
| | 250 | navpoint->input = input; |
| | 251 | navpoint->dev = &pdev->dev; |
| | 252 | navpoint->gpio = pdata->gpio; |
| | 253 | |
| | 254 | input->name = pdev->name; |
| | 255 | input->dev.parent = &pdev->dev; |
| | 256 | |
| | 257 | __set_bit(EV_KEY, input->evbit); |
| | 258 | __set_bit(EV_ABS, input->evbit); |
| | 259 | __set_bit(BTN_LEFT, input->keybit); |
| | 260 | __set_bit(BTN_TOUCH, input->keybit); |
| | 261 | __set_bit(BTN_TOOL_FINGER, input->keybit); |
| | 262 | |
| | 263 | input_set_abs_params(input, ABS_X, |
| | 264 | NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); |
| | 265 | input_set_abs_params(input, ABS_Y, |
| | 266 | NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); |
| | 267 | input_set_abs_params(input, ABS_PRESSURE, |
| | 268 | NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, |
| | 269 | 0, 0); |
| | 270 | |
| | 271 | input->open = navpoint_open; |
| | 272 | input->close = navpoint_close; |
| | 273 | |
| | 274 | input_set_drvdata(input, navpoint); |
| | 275 | |
| | 276 | error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); |
| | 277 | if (error) |
| | 278 | goto err_free_mem; |
| | 279 | |
| | 280 | error = input_register_device(input); |
| | 281 | if (error) |
| | 282 | goto err_free_irq; |
| | 283 | |
| | 284 | platform_set_drvdata(pdev, navpoint); |
| | 285 | dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); |
| | 286 | |
| | 287 | return 0; |
| | 288 | |
| | 289 | err_free_irq: |
| | 290 | free_irq(ssp->irq, &pdev->dev); |
| | 291 | err_free_mem: |
| | 292 | input_free_device(input); |
| | 293 | kfree(navpoint); |
| | 294 | pxa_ssp_free(ssp); |
| | 295 | err_free_gpio: |
| | 296 | if (gpio_is_valid(pdata->gpio)) |
| | 297 | gpio_free(pdata->gpio); |
| | 298 | |
| | 299 | return error; |
| | 300 | } |
| | 301 | |
| | 302 | static int __devexit navpoint_remove(struct platform_device *pdev) |
| | 303 | { |
| | 304 | const struct navpoint_platform_data *pdata = |
| | 305 | dev_get_platdata(&pdev->dev); |
| | 306 | struct navpoint *navpoint = platform_get_drvdata(pdev); |
| | 307 | struct ssp_device *ssp = navpoint->ssp; |
| | 308 | |
| | 309 | free_irq(ssp->irq, navpoint); |
| | 310 | |
| | 311 | input_unregister_device(navpoint->input); |
| | 312 | kfree(navpoint); |
| | 313 | |
| | 314 | pxa_ssp_free(ssp); |
| | 315 | |
| | 316 | if (gpio_is_valid(pdata->gpio)) |
| | 317 | gpio_free(pdata->gpio); |
| | 318 | |
| | 319 | return 0; |
| | 320 | } |
| | 321 | |
| | 322 | #ifdef CONFIG_PM_SLEEP |
| | 323 | static int navpoint_suspend(struct device *dev) |
| | 324 | { |
| | 325 | struct platform_device *pdev = to_platform_device(dev); |
| | 326 | struct navpoint *navpoint = platform_get_drvdata(pdev); |
| | 327 | struct input_dev *input = navpoint->input; |
| | 328 | |
| | 329 | mutex_lock(&input->mutex); |
| | 330 | if (input->users) |
| | 331 | navpoint_down(navpoint); |
| | 332 | mutex_unlock(&input->mutex); |
| | 333 | |
| | 334 | return 0; |
| | 335 | } |
| | 336 | |
| | 337 | static int navpoint_resume(struct device *dev) |
| | 338 | { |
| | 339 | struct platform_device *pdev = to_platform_device(dev); |
| | 340 | struct navpoint *navpoint = platform_get_drvdata(pdev); |
| | 341 | struct input_dev *input = navpoint->input; |
| | 342 | |
| | 343 | mutex_lock(&input->mutex); |
| | 344 | if (input->users) |
| | 345 | navpoint_up(navpoint); |
| | 346 | mutex_unlock(&input->mutex); |
| | 347 | |
| | 348 | return 0; |
| | 349 | } |
| | 350 | #endif |
| | 351 | |
| | 352 | static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); |
| | 353 | |
| | 354 | static struct platform_driver navpoint_driver = { |
| | 355 | .probe = navpoint_probe, |
| | 356 | .remove = __devexit_p(navpoint_remove), |
| | 357 | .driver = { |
| | 358 | .name = "navpoint", |
| | 359 | .owner = THIS_MODULE, |
| | 360 | .pm = &navpoint_pm_ops, |
| | 361 | }, |
| | 362 | }; |
| | 363 | |
| | 364 | module_platform_driver(navpoint_driver); |
| | 365 | |
| | 366 | MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); |
| | 367 | MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); |
| | 368 | MODULE_LICENSE("GPL"); |
| | 369 | MODULE_ALIAS("platform:navpoint"); |