1/* 2 * Driver for the enhanced rotary controller on pxa930 and pxa935 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/interrupt.h> 12#include <linux/input.h> 13#include <linux/platform_device.h> 14#include <linux/io.h> 15#include <linux/slab.h> 16 17#include <linux/platform_data/keyboard-pxa930_rotary.h> 18 19#define SBCR (0x04) 20#define ERCR (0x0c) 21 22#define SBCR_ERSB (1 << 5) 23 24struct pxa930_rotary { 25 struct input_dev *input_dev; 26 void __iomem *mmio_base; 27 int last_ercr; 28 29 struct pxa930_rotary_platform_data *pdata; 30}; 31 32static void clear_sbcr(struct pxa930_rotary *r) 33{ 34 uint32_t sbcr = __raw_readl(r->mmio_base + SBCR); 35 36 __raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR); 37 __raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR); 38} 39 40static irqreturn_t rotary_irq(int irq, void *dev_id) 41{ 42 struct pxa930_rotary *r = dev_id; 43 struct pxa930_rotary_platform_data *pdata = r->pdata; 44 int ercr, delta, key; 45 46 ercr = __raw_readl(r->mmio_base + ERCR) & 0xf; 47 clear_sbcr(r); 48 49 delta = ercr - r->last_ercr; 50 if (delta == 0) 51 return IRQ_HANDLED; 52 53 r->last_ercr = ercr; 54 55 if (pdata->up_key && pdata->down_key) { 56 key = (delta > 0) ? pdata->up_key : pdata->down_key; 57 input_report_key(r->input_dev, key, 1); 58 input_sync(r->input_dev); 59 input_report_key(r->input_dev, key, 0); 60 } else 61 input_report_rel(r->input_dev, pdata->rel_code, delta); 62 63 input_sync(r->input_dev); 64 65 return IRQ_HANDLED; 66} 67 68static int pxa930_rotary_open(struct input_dev *dev) 69{ 70 struct pxa930_rotary *r = input_get_drvdata(dev); 71 72 clear_sbcr(r); 73 74 return 0; 75} 76 77static void pxa930_rotary_close(struct input_dev *dev) 78{ 79 struct pxa930_rotary *r = input_get_drvdata(dev); 80 81 clear_sbcr(r); 82} 83 84static int pxa930_rotary_probe(struct platform_device *pdev) 85{ 86 struct pxa930_rotary_platform_data *pdata = 87 dev_get_platdata(&pdev->dev); 88 struct pxa930_rotary *r; 89 struct input_dev *input_dev; 90 struct resource *res; 91 int irq; 92 int err; 93 94 irq = platform_get_irq(pdev, 0); 95 if (irq < 0) { 96 dev_err(&pdev->dev, "no irq for rotary controller\n"); 97 return -ENXIO; 98 } 99 100 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 101 if (!res) { 102 dev_err(&pdev->dev, "no I/O memory defined\n"); 103 return -ENXIO; 104 } 105 106 if (!pdata) { 107 dev_err(&pdev->dev, "no platform data defined\n"); 108 return -EINVAL; 109 } 110 111 r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL); 112 if (!r) 113 return -ENOMEM; 114 115 r->mmio_base = ioremap_nocache(res->start, resource_size(res)); 116 if (r->mmio_base == NULL) { 117 dev_err(&pdev->dev, "failed to remap IO memory\n"); 118 err = -ENXIO; 119 goto failed_free; 120 } 121 122 r->pdata = pdata; 123 platform_set_drvdata(pdev, r); 124 125 /* allocate and register the input device */ 126 input_dev = input_allocate_device(); 127 if (!input_dev) { 128 dev_err(&pdev->dev, "failed to allocate input device\n"); 129 err = -ENOMEM; 130 goto failed_free_io; 131 } 132 133 input_dev->name = pdev->name; 134 input_dev->id.bustype = BUS_HOST; 135 input_dev->open = pxa930_rotary_open; 136 input_dev->close = pxa930_rotary_close; 137 input_dev->dev.parent = &pdev->dev; 138 139 if (pdata->up_key && pdata->down_key) { 140 __set_bit(pdata->up_key, input_dev->keybit); 141 __set_bit(pdata->down_key, input_dev->keybit); 142 __set_bit(EV_KEY, input_dev->evbit); 143 } else { 144 __set_bit(pdata->rel_code, input_dev->relbit); 145 __set_bit(EV_REL, input_dev->evbit); 146 } 147 148 r->input_dev = input_dev; 149 input_set_drvdata(input_dev, r); 150 151 err = request_irq(irq, rotary_irq, 0, 152 "enhanced rotary", r); 153 if (err) { 154 dev_err(&pdev->dev, "failed to request IRQ\n"); 155 goto failed_free_input; 156 } 157 158 err = input_register_device(input_dev); 159 if (err) { 160 dev_err(&pdev->dev, "failed to register input device\n"); 161 goto failed_free_irq; 162 } 163 164 return 0; 165 166failed_free_irq: 167 free_irq(irq, r); 168failed_free_input: 169 input_free_device(input_dev); 170failed_free_io: 171 iounmap(r->mmio_base); 172failed_free: 173 kfree(r); 174 return err; 175} 176 177static int pxa930_rotary_remove(struct platform_device *pdev) 178{ 179 struct pxa930_rotary *r = platform_get_drvdata(pdev); 180 181 free_irq(platform_get_irq(pdev, 0), r); 182 input_unregister_device(r->input_dev); 183 iounmap(r->mmio_base); 184 kfree(r); 185 186 return 0; 187} 188 189static struct platform_driver pxa930_rotary_driver = { 190 .driver = { 191 .name = "pxa930-rotary", 192 }, 193 .probe = pxa930_rotary_probe, 194 .remove = pxa930_rotary_remove, 195}; 196module_platform_driver(pxa930_rotary_driver); 197 198MODULE_LICENSE("GPL"); 199MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller"); 200MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>"); 201