1/* 2 * PXA930 track ball mouse driver 3 * 4 * Copyright (C) 2007 Marvell International Ltd. 5 * 2008-02-28: Yong Yao <yaoyong@marvell.com> 6 * initial version 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13#include <linux/input.h> 14#include <linux/interrupt.h> 15#include <linux/module.h> 16#include <linux/platform_device.h> 17#include <linux/delay.h> 18#include <linux/io.h> 19#include <linux/slab.h> 20 21#include <mach/hardware.h> 22#include <linux/platform_data/mouse-pxa930_trkball.h> 23 24/* Trackball Controller Register Definitions */ 25#define TBCR (0x000C) 26#define TBCNTR (0x0010) 27#define TBSBC (0x0014) 28 29#define TBCR_TBRST (1 << 1) 30#define TBCR_TBSB (1 << 10) 31 32#define TBCR_Y_FLT(n) (((n) & 0xf) << 6) 33#define TBCR_X_FLT(n) (((n) & 0xf) << 2) 34 35#define TBCNTR_YM(n) (((n) >> 24) & 0xff) 36#define TBCNTR_YP(n) (((n) >> 16) & 0xff) 37#define TBCNTR_XM(n) (((n) >> 8) & 0xff) 38#define TBCNTR_XP(n) ((n) & 0xff) 39 40#define TBSBC_TBSBC (0x1) 41 42struct pxa930_trkball { 43 struct pxa930_trkball_platform_data *pdata; 44 45 /* Memory Mapped Register */ 46 struct resource *mem; 47 void __iomem *mmio_base; 48 49 struct input_dev *input; 50}; 51 52static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id) 53{ 54 struct pxa930_trkball *trkball = dev_id; 55 struct input_dev *input = trkball->input; 56 int tbcntr, x, y; 57 58 /* According to the spec software must read TBCNTR twice: 59 * if the read value is the same, the reading is valid 60 */ 61 tbcntr = __raw_readl(trkball->mmio_base + TBCNTR); 62 63 if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) { 64 x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2; 65 y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2; 66 67 input_report_rel(input, REL_X, x); 68 input_report_rel(input, REL_Y, y); 69 input_sync(input); 70 } 71 72 __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC); 73 __raw_writel(0, trkball->mmio_base + TBSBC); 74 75 return IRQ_HANDLED; 76} 77 78/* For TBCR, we need to wait for a while to make sure it has been modified. */ 79static int write_tbcr(struct pxa930_trkball *trkball, int v) 80{ 81 int i = 100; 82 83 __raw_writel(v, trkball->mmio_base + TBCR); 84 85 while (--i) { 86 if (__raw_readl(trkball->mmio_base + TBCR) == v) 87 break; 88 msleep(1); 89 } 90 91 if (i == 0) { 92 pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v); 93 return -ETIMEDOUT; 94 } 95 96 return 0; 97} 98 99static void pxa930_trkball_config(struct pxa930_trkball *trkball) 100{ 101 uint32_t tbcr; 102 103 /* According to spec, need to write the filters of x,y to 0xf first! */ 104 tbcr = __raw_readl(trkball->mmio_base + TBCR); 105 write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf)); 106 write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) | 107 TBCR_Y_FLT(trkball->pdata->y_filter)); 108 109 /* According to spec, set TBCR_TBRST first, before clearing it! */ 110 tbcr = __raw_readl(trkball->mmio_base + TBCR); 111 write_tbcr(trkball, tbcr | TBCR_TBRST); 112 write_tbcr(trkball, tbcr & ~TBCR_TBRST); 113 114 __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC); 115 __raw_writel(0, trkball->mmio_base + TBSBC); 116 117 pr_debug("%s: final TBCR=%x!\n", __func__, 118 __raw_readl(trkball->mmio_base + TBCR)); 119} 120 121static int pxa930_trkball_open(struct input_dev *dev) 122{ 123 struct pxa930_trkball *trkball = input_get_drvdata(dev); 124 125 pxa930_trkball_config(trkball); 126 127 return 0; 128} 129 130static void pxa930_trkball_disable(struct pxa930_trkball *trkball) 131{ 132 uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR); 133 134 /* Held in reset, gate the 32-KHz input clock off */ 135 write_tbcr(trkball, tbcr | TBCR_TBRST); 136} 137 138static void pxa930_trkball_close(struct input_dev *dev) 139{ 140 struct pxa930_trkball *trkball = input_get_drvdata(dev); 141 142 pxa930_trkball_disable(trkball); 143} 144 145static int pxa930_trkball_probe(struct platform_device *pdev) 146{ 147 struct pxa930_trkball *trkball; 148 struct input_dev *input; 149 struct resource *res; 150 int irq, error; 151 152 irq = platform_get_irq(pdev, 0); 153 if (irq < 0) { 154 dev_err(&pdev->dev, "failed to get trkball irq\n"); 155 return -ENXIO; 156 } 157 158 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 159 if (!res) { 160 dev_err(&pdev->dev, "failed to get register memory\n"); 161 return -ENXIO; 162 } 163 164 trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL); 165 if (!trkball) 166 return -ENOMEM; 167 168 trkball->pdata = dev_get_platdata(&pdev->dev); 169 if (!trkball->pdata) { 170 dev_err(&pdev->dev, "no platform data defined\n"); 171 error = -EINVAL; 172 goto failed; 173 } 174 175 trkball->mmio_base = ioremap_nocache(res->start, resource_size(res)); 176 if (!trkball->mmio_base) { 177 dev_err(&pdev->dev, "failed to ioremap registers\n"); 178 error = -ENXIO; 179 goto failed; 180 } 181 182 /* held the module in reset, will be enabled in open() */ 183 pxa930_trkball_disable(trkball); 184 185 error = request_irq(irq, pxa930_trkball_interrupt, 0, 186 pdev->name, trkball); 187 if (error) { 188 dev_err(&pdev->dev, "failed to request irq: %d\n", error); 189 goto failed_free_io; 190 } 191 192 platform_set_drvdata(pdev, trkball); 193 194 input = input_allocate_device(); 195 if (!input) { 196 dev_err(&pdev->dev, "failed to allocate input device\n"); 197 error = -ENOMEM; 198 goto failed_free_irq; 199 } 200 201 input->name = pdev->name; 202 input->id.bustype = BUS_HOST; 203 input->open = pxa930_trkball_open; 204 input->close = pxa930_trkball_close; 205 input->dev.parent = &pdev->dev; 206 input_set_drvdata(input, trkball); 207 208 trkball->input = input; 209 210 input_set_capability(input, EV_REL, REL_X); 211 input_set_capability(input, EV_REL, REL_Y); 212 213 error = input_register_device(input); 214 if (error) { 215 dev_err(&pdev->dev, "unable to register input device\n"); 216 goto failed_free_input; 217 } 218 219 return 0; 220 221failed_free_input: 222 input_free_device(input); 223failed_free_irq: 224 free_irq(irq, trkball); 225failed_free_io: 226 iounmap(trkball->mmio_base); 227failed: 228 kfree(trkball); 229 return error; 230} 231 232static int pxa930_trkball_remove(struct platform_device *pdev) 233{ 234 struct pxa930_trkball *trkball = platform_get_drvdata(pdev); 235 int irq = platform_get_irq(pdev, 0); 236 237 input_unregister_device(trkball->input); 238 free_irq(irq, trkball); 239 iounmap(trkball->mmio_base); 240 kfree(trkball); 241 242 return 0; 243} 244 245static struct platform_driver pxa930_trkball_driver = { 246 .driver = { 247 .name = "pxa930-trkball", 248 }, 249 .probe = pxa930_trkball_probe, 250 .remove = pxa930_trkball_remove, 251}; 252module_platform_driver(pxa930_trkball_driver); 253 254MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>"); 255MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver"); 256MODULE_LICENSE("GPL"); 257