Path: blob/master/drivers/input/mouse/pxa930_trkball.c
15111 views
/*1* PXA930 track ball mouse driver2*3* Copyright (C) 2007 Marvell International Ltd.4* 2008-02-28: Yong Yao <[email protected]>5* initial version6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*/1112#include <linux/init.h>13#include <linux/input.h>14#include <linux/version.h>15#include <linux/interrupt.h>16#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/delay.h>19#include <linux/io.h>20#include <linux/slab.h>2122#include <mach/hardware.h>23#include <mach/pxa930_trkball.h>2425/* Trackball Controller Register Definitions */26#define TBCR (0x000C)27#define TBCNTR (0x0010)28#define TBSBC (0x0014)2930#define TBCR_TBRST (1 << 1)31#define TBCR_TBSB (1 << 10)3233#define TBCR_Y_FLT(n) (((n) & 0xf) << 6)34#define TBCR_X_FLT(n) (((n) & 0xf) << 2)3536#define TBCNTR_YM(n) (((n) >> 24) & 0xff)37#define TBCNTR_YP(n) (((n) >> 16) & 0xff)38#define TBCNTR_XM(n) (((n) >> 8) & 0xff)39#define TBCNTR_XP(n) ((n) & 0xff)4041#define TBSBC_TBSBC (0x1)4243struct pxa930_trkball {44struct pxa930_trkball_platform_data *pdata;4546/* Memory Mapped Register */47struct resource *mem;48void __iomem *mmio_base;4950struct input_dev *input;51};5253static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)54{55struct pxa930_trkball *trkball = dev_id;56struct input_dev *input = trkball->input;57int tbcntr, x, y;5859/* According to the spec software must read TBCNTR twice:60* if the read value is the same, the reading is valid61*/62tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);6364if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {65x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;66y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;6768input_report_rel(input, REL_X, x);69input_report_rel(input, REL_Y, y);70input_sync(input);71}7273__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);74__raw_writel(0, trkball->mmio_base + TBSBC);7576return IRQ_HANDLED;77}7879/* For TBCR, we need to wait for a while to make sure it has been modified. */80static int write_tbcr(struct pxa930_trkball *trkball, int v)81{82int i = 100;8384__raw_writel(v, trkball->mmio_base + TBCR);8586while (--i) {87if (__raw_readl(trkball->mmio_base + TBCR) == v)88break;89msleep(1);90}9192if (i == 0) {93pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);94return -ETIMEDOUT;95}9697return 0;98}99100static void pxa930_trkball_config(struct pxa930_trkball *trkball)101{102uint32_t tbcr;103104/* According to spec, need to write the filters of x,y to 0xf first! */105tbcr = __raw_readl(trkball->mmio_base + TBCR);106write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));107write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |108TBCR_Y_FLT(trkball->pdata->y_filter));109110/* According to spec, set TBCR_TBRST first, before clearing it! */111tbcr = __raw_readl(trkball->mmio_base + TBCR);112write_tbcr(trkball, tbcr | TBCR_TBRST);113write_tbcr(trkball, tbcr & ~TBCR_TBRST);114115__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);116__raw_writel(0, trkball->mmio_base + TBSBC);117118pr_debug("%s: final TBCR=%x!\n", __func__,119__raw_readl(trkball->mmio_base + TBCR));120}121122static int pxa930_trkball_open(struct input_dev *dev)123{124struct pxa930_trkball *trkball = input_get_drvdata(dev);125126pxa930_trkball_config(trkball);127128return 0;129}130131static void pxa930_trkball_disable(struct pxa930_trkball *trkball)132{133uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);134135/* Held in reset, gate the 32-KHz input clock off */136write_tbcr(trkball, tbcr | TBCR_TBRST);137}138139static void pxa930_trkball_close(struct input_dev *dev)140{141struct pxa930_trkball *trkball = input_get_drvdata(dev);142143pxa930_trkball_disable(trkball);144}145146static int __devinit pxa930_trkball_probe(struct platform_device *pdev)147{148struct pxa930_trkball *trkball;149struct input_dev *input;150struct resource *res;151int irq, error;152153irq = platform_get_irq(pdev, 0);154if (irq < 0) {155dev_err(&pdev->dev, "failed to get trkball irq\n");156return -ENXIO;157}158159res = platform_get_resource(pdev, IORESOURCE_MEM, 0);160if (!res) {161dev_err(&pdev->dev, "failed to get register memory\n");162return -ENXIO;163}164165trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);166if (!trkball)167return -ENOMEM;168169trkball->pdata = pdev->dev.platform_data;170if (!trkball->pdata) {171dev_err(&pdev->dev, "no platform data defined\n");172error = -EINVAL;173goto failed;174}175176trkball->mmio_base = ioremap_nocache(res->start, resource_size(res));177if (!trkball->mmio_base) {178dev_err(&pdev->dev, "failed to ioremap registers\n");179error = -ENXIO;180goto failed;181}182183/* held the module in reset, will be enabled in open() */184pxa930_trkball_disable(trkball);185186error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED,187pdev->name, trkball);188if (error) {189dev_err(&pdev->dev, "failed to request irq: %d\n", error);190goto failed_free_io;191}192193platform_set_drvdata(pdev, trkball);194195input = input_allocate_device();196if (!input) {197dev_err(&pdev->dev, "failed to allocate input device\n");198error = -ENOMEM;199goto failed_free_irq;200}201202input->name = pdev->name;203input->id.bustype = BUS_HOST;204input->open = pxa930_trkball_open;205input->close = pxa930_trkball_close;206input->dev.parent = &pdev->dev;207input_set_drvdata(input, trkball);208209trkball->input = input;210211input_set_capability(input, EV_REL, REL_X);212input_set_capability(input, EV_REL, REL_Y);213214error = input_register_device(input);215if (error) {216dev_err(&pdev->dev, "unable to register input device\n");217goto failed_free_input;218}219220return 0;221222failed_free_input:223input_free_device(input);224failed_free_irq:225free_irq(irq, trkball);226failed_free_io:227iounmap(trkball->mmio_base);228failed:229kfree(trkball);230return error;231}232233static int __devexit pxa930_trkball_remove(struct platform_device *pdev)234{235struct pxa930_trkball *trkball = platform_get_drvdata(pdev);236int irq = platform_get_irq(pdev, 0);237238input_unregister_device(trkball->input);239free_irq(irq, trkball);240iounmap(trkball->mmio_base);241kfree(trkball);242243return 0;244}245246static struct platform_driver pxa930_trkball_driver = {247.driver = {248.name = "pxa930-trkball",249},250.probe = pxa930_trkball_probe,251.remove = __devexit_p(pxa930_trkball_remove),252};253254static int __init pxa930_trkball_init(void)255{256return platform_driver_register(&pxa930_trkball_driver);257}258259static void __exit pxa930_trkball_exit(void)260{261platform_driver_unregister(&pxa930_trkball_driver);262}263264module_init(pxa930_trkball_init);265module_exit(pxa930_trkball_exit);266267MODULE_AUTHOR("Yong Yao <[email protected]>");268MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");269MODULE_LICENSE("GPL");270271272