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