Is there a linux driver for the eQEP device?
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Is there a linux driver for the eQEP device?
Hello,
Is there a Linux driver for the Sitara eQEP?
I'm using a Beaglebone for code development.
Thanks,
Max
Hi,
The PWMSS driver in the current PSP only supports these features: http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver%27s_Guide
eQEP is not yet supported in the Linux SDK.
Best regards,
Miroslav
Look at this:http://processors.wiki.ti.com/index.php/Omapl137_linux_eqep_driver
and here is the webex presentation for the driver (See bottom of page): http://processors.wiki.ti.com/index.php/OMAPL1_PSP_WebEx_Presentations#OMAPL1_Linux_eQEP_Driver_WebEx_Presentations_-_In_Parts - open this is Google Chrome!
From a cursory look, the OMAPl137 eQEP modules looks identical to the sitara one (i.e. the register map is the same) So you should be able to take the OMAPl137 kernel code and port it to sitara. I would be curious to know about your progress, since I'd like to use the eqep in the near future as well.
Hi Miroslav,
Thanks for your response. Unfortunately the administrator deleted my post because somebody else asked a similar question some minutes LATER and I never got your answer. I don't think this was good idea.
Is there any plan to support the eQEP?
Would you suggest using the driver from the Omapl137 as the other response suggests?
http://processors.wiki.ti.com/index.php/Omapl137_linux_eqep_driver
Regards,
Max
Hi Max,
The Sitara AM335x SDK team is constantly working to improve the SDK and to provide control over all the functionality of the peripherals, so support for the eQEP module will definitely be coming in a future release of the SDK, but unfortunately I don't have any information on when exactly is this going to happen.
As for the OMAPL137 eQEP driver, it seems like the eQEP module is the same for the Sitara AM335x (please refer to section 15.4 of the AM335x TRM), so the above suggestion to use the OMAPL137 driver and port it to the Sitara AM335x does make sense.
Best regards,
Miroslav
Hi Miroslav,
Thanks for the quick response.
I'm going to try to "port" the OMAPL137 eQEP driver, but if it was that easy I imagine that the TI SDK people would have ported it already... what do you think?
Best regards,
Max
I'm afraid I don't have the answer to your question. I don't know how the SDK team have ranked their priorities. Perhaps they are already working on this, but I assume some testing and verification procedures are required before officially releasing the driver.
Best regards,
Miroslav
I was able to download the kernel for the omapl137 from this link: http://processors.wiki.ti.com/index.php/Installing_the_Software_for_OMAP-L137
Its hard to wade through all the files, but the kernel is in the mvl_5_0_0_demo_lsp_setuplinux_02_20_#_#.bin executable. You have to run this in linux to get to it. Attached is the patch file and the eqep.c driver file I was able to extract from it.
#! /usr/bin/env bash # Patch: -pro_input_misc_add_eqep_driver # Date: Thu Feb 19 15:11:58 2009 # Source: MontaVista Software, Inc. # MR: 29030 # Type: Enhancement # Disposition: needs submitting to linux community # Signed-off-by: Mark A. Greer <mgreer@mvista.com> # Description: # # eQEP: Add driver for TI eQEP controller # # Add a driver for the TI TMS320C647x enhanced Quadrature Encoder # Pulse (eQEP) controller can monitor (and in some cases control) # electric motors, volume control knobs, etc. # PATCHNUM=2259 LSPINFO=include/linux/lsppatchlevel.h TMPFILE=/tmp/mvl_patch_$$ function dopatch() { patch $* >${TMPFILE} 2>&1 <<"EOF" Source: MontaVista Software, Inc. MR: 29030 Type: Enhancement Disposition: needs submitting to linux community Signed-off-by: Mark A. Greer <mgreer@mvista.com> Description: eQEP: Add driver for TI eQEP controller Add a driver for the TI TMS320C647x enhanced Quadrature Encoder Pulse (eQEP) controller can monitor (and in some cases control) electric motors, volume control knobs, etc. Index: linux-2.6.18/drivers/input/misc/Kconfig =================================================================== --- linux-2.6.18.orig/drivers/input/misc/Kconfig +++ linux-2.6.18/drivers/input/misc/Kconfig @@ -79,4 +79,17 @@ config HP_SDC_RTC Say Y here if you want to support the built-in real time clock of the HP SDC controller. +config INPUT_EQEP + tristate "TI enhanced Quadrature Encoder Pulse (eQEP) support" + depends on ARCH_DAVINCI + select INPUT_EVDEV + help + Say Y here if you want to support the TI enhanced Quadrature + Encoder Pulse (eQEP) controller. The eQEP is used to interface + with a linear or rotary incremental encoder to get position, + direction, and speed. This is used for motion and position + control. An example would be monitoring a volume control knob + on a home theater system or monitoring the speed of an electric + motor. + endif Index: linux-2.6.18/drivers/input/misc/Makefile =================================================================== --- linux-2.6.18.orig/drivers/input/misc/Makefile +++ linux-2.6.18/drivers/input/misc/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_EQEP) += eqep.o Index: linux-2.6.18/drivers/input/misc/eqep.c =================================================================== --- /dev/null +++ linux-2.6.18/drivers/input/misc/eqep.c @@ -0,0 +1,676 @@ +/* + * TI eQEP driver + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 2008 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +/* + * The eQEP supports numerous types of encoders/applications, several + * combinations of when to latch the position count, timers, etc. + * Too numerous to try to guess in a driver. So, instead of trying + * to guess, the driver provides a very low-level interface to the + * user. The hardware will be configured based on values that + * the application provides through sysfs before opening the device. + * On each interrupt, the driver sends up all of the latched register + * values along with the values of the interrupt flag and status registers. + * Therefore, it is up to the application to know how it wants to interface + * to the external encoder how it wants the eQEP to act. + * + * When an interrupt occurs, the values of QFLG, QEPSTS, QPOSLAT, QPOSILAT, + * QPOSSLAT, QCTMRLAT, and QCPRDLAT registers will be sent up as + * EV_MSC/MSC_RAW events. An EV_SYN/SYN_REPORT event will be sent up + * afterwards to delineate one set of interrupt events from another. + */ +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/io.h> + + +#define EQEP_QPOSCNT 0x00 +#define EQEP_QPOSINIT 0x04 +#define EQEP_QPOSMAX 0x08 +#define EQEP_QPOSCMP 0x0c +#define EQEP_QPOSILAT 0x10 +#define EQEP_QPOSSLAT 0x14 +#define EQEP_QPOSLAT 0x18 +#define EQEP_QUTMR 0x1c +#define EQEP_QUPRD 0x20 +#define EQEP_QWDTMR 0x24 +#define EQEP_QWDPRD 0x26 +#define EQEP_QDECCTL 0x28 +#define EQEP_QDECCTL_QSRC(x) (((x) & 0x3) << 14) +#define EQEP_QDECCTL_SOEN (1 << 13) +#define EQEP_QDECCTL_SPSEL (1 << 12) +#define EQEP_QDECCTL_XCR (1 << 11) +#define EQEP_QDECCTL_SWAP (1 << 10) +#define EQEP_QDECCTL_IGATE (1 << 9) +#define EQEP_QDECCTL_QAP (1 << 8) +#define EQEP_QDECCTL_QBP (1 << 7) +#define EQEP_QDECCTL_QIP (1 << 6) +#define EQEP_QDECCTL_QSP (1 << 5) +#define EQEP_QEPCTL 0x2a +#define EQEP_QEPCTL_PCRM(x) (((x) & 0x3) << 12) +#define EQEP_QEPCTL_SEI(x) (((x) & 0x3) << 10) +#define EQEP_QEPCTL_IEI(x) (((x) & 0x3) << 8) +#define EQEP_QEPCTL_SWI (1 << 7) +#define EQEP_QEPCTL_SEL (1 << 6) +#define EQEP_QEPCTL_IEL(x) (((x) & 0x3) << 4) +#define EQEP_QEPCTL_PHEN (1 << 3) +#define EQEP_QEPCTL_QCLM (1 << 2) +#define EQEP_QEPCTL_UTE (1 << 1) +#define EQEP_QEPCTL_WDE (1 << 0) +#define EQEP_QCAPCTL 0x2c +#define EQEP_QPOSCTL 0x2e +#define EQEP_QEINT 0x30 +#define EQEP_QFLG 0x32 +#define EQEP_QCLR 0x34 +#define EQEP_QFRC 0x36 +#define EQEP_INT_UTO (1 << 11) /* Same for all intr regs */ +#define EQEP_INT_IEL (1 << 10) +#define EQEP_INT_SEL (1 << 9) +#define EQEP_INT_PCM (1 << 8) +#define EQEP_INT_PCR (1 << 7) +#define EQEP_INT_PCO (1 << 6) +#define EQEP_INT_PCU (1 << 5) +#define EQEP_INT_WTO (1 << 4) +#define EQEP_INT_QDC (1 << 3) +#define EQEP_INT_PHE (1 << 2) +#define EQEP_INT_PCE (1 << 1) +#define EQEP_INT_INT (1 << 0) +#define EQEP_INT_ENABLE_ALL (EQEP_INT_UTO | EQEP_INT_IEL \ + | EQEP_INT_SEL | EQEP_INT_PCM | EQEP_INT_PCR \ + | EQEP_INT_PCO | EQEP_INT_PCU | EQEP_INT_WTO \ + | EQEP_INT_QDC | EQEP_INT_PHE | EQEP_INT_PCE) +#define EQEP_INT_MASK (EQEP_INT_ENABLE_ALL | EQEP_INT_INT) +#define EQEP_QEPSTS 0x38 +#define EQEP_QEPSTS_UPEVNT (1 << 7) +#define EQEP_QEPSTS_FDF (1 << 6) +#define EQEP_QEPSTS_QDF (1 << 5) +#define EQEP_QEPSTS_QDLF (1 << 4) +#define EQEP_QEPSTS_COEF (1 << 3) +#define EQEP_QEPSTS_CDEF (1 << 2) +#define EQEP_QEPSTS_FIMF (1 << 1) +#define EQEP_QEPSTS_PCEF (1 << 0) +#define EQEP_QCTMR 0x3a +#define EQEP_QCPRD 0x3c +#define EQEP_QCTMRLAT 0x3e +#define EQEP_QCPRDLAT 0x40 +#define EQEP_REVID 0x5c + +#define EQEP_INPUT_DEV_PHYS_SIZE 32 + +enum eqeq_irq_data { + ID_QPOSLAT, + ID_QPOSILAT, + ID_QPOSSLAT, + ID_QCTMRLAT, + ID_QCPRDLAT, + ID_NUM, +}; + +struct eqep_info { + struct input_dev *input_dev; + struct resource *res; + resource_size_t pbase; + void __iomem *vbase; + size_t base_size; + int irq; + u16 qeint_save; + u32 irq_data[ID_NUM]; + char input_dev_buf[EQEP_INPUT_DEV_PHYS_SIZE]; +}; + +#ifdef CONFIG_SYSFS +struct eqep_name_map { + char *attr_name; + u32 offset; + u8 reg_size; +}; + +static struct eqep_name_map eqep_name_map[] = { + { + .attr_name = "qposcnt", + .offset = EQEP_QPOSCNT, + .reg_size = 32, + }, + { + .attr_name = "qposinit", + .offset = EQEP_QPOSINIT, + .reg_size = 32, + }, + { + .attr_name = "qposmax", + .offset = EQEP_QPOSMAX, + .reg_size = 32, + }, + { + .attr_name = "qposcmp", + .offset = EQEP_QPOSCMP, + .reg_size = 32, + }, + { + .attr_name = "qutmr", + .offset = EQEP_QUTMR, + .reg_size = 32, + }, + { + .attr_name = "quprd", + .offset = EQEP_QUPRD, + .reg_size = 32, + }, + { + .attr_name = "qwdtmr", + .offset = EQEP_QWDTMR, + .reg_size = 16, + }, + { + .attr_name = "qwdprd", + .offset = EQEP_QWDPRD, + .reg_size = 16, + }, + { + .attr_name = "qdecctl", + .offset = EQEP_QDECCTL, + .reg_size = 16, + }, + { + .attr_name = "qepctl", + .offset = EQEP_QEPCTL, + .reg_size = 16, + }, + { + .attr_name = "qcapctl", + .offset = EQEP_QCAPCTL, + .reg_size = 16, + }, + { + .attr_name = "qposctl", + .offset = EQEP_QPOSCTL, + .reg_size = 16, + }, + { + .attr_name = "qeint", + .offset = EQEP_QEINT, + .reg_size = 16, + }, + { + .attr_name = "qctmr", + .offset = EQEP_QCTMR, + .reg_size = 16, + }, + { + .attr_name = "qcprd", + .offset = EQEP_QCPRD, + .reg_size = 16, + }, + { + .attr_name = "revid", + .offset = EQEP_REVID, + .reg_size = 32, + }, +}; + +static int eqep_find_offset(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(eqep_name_map); i++) + if (!strcmp(name, eqep_name_map[i].attr_name)) + return i; + + return -1; +} + +static ssize_t eqep_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct eqep_info *eip = dev_get_drvdata(dev); + struct eqep_name_map *nmp; + int posn; + u32 v; + + if (eip == NULL) + goto err_out; + + posn = eqep_find_offset(attr->attr.name); + if (posn < 0) + goto err_out; + + nmp = &eqep_name_map[posn]; + + if (nmp->reg_size == 16) + v = ioread16(eip->vbase + nmp->offset); + else + v = ioread32(eip->vbase + nmp->offset); + + return sprintf(buf, "0x%08x", v); + +err_out: + return -ENXIO; +} + +static ssize_t eqep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct eqep_info *eip = dev_get_drvdata(dev); + struct eqep_name_map *nmp; + int posn, ret; + unsigned long v; + + if (eip == NULL) + goto err_out; + + posn = eqep_find_offset(attr->attr.name); + if (posn < 0) + goto err_out; + + nmp = &eqep_name_map[posn]; + + ret = strict_strtoul(buf, 0, &v); + if (ret < 0) + goto err_out; + + if (nmp->reg_size == 16) + iowrite16(v, eip->vbase + nmp->offset); + else + iowrite32(v, eip->vbase + nmp->offset); + + return strlen(buf); + +err_out: + return -ENXIO; +} +static DEVICE_ATTR(qposcnt, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qposinit, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qposmax, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qposcmp, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qutmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(quprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qwdtmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qwdprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qdecctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qepctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qcapctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qposctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qctmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(qcprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); +static DEVICE_ATTR(revid, S_IRUGO, eqep_show, NULL); + +static ssize_t eqep_qeint_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct eqep_info *eip = dev_get_drvdata(dev); + + if (eip == NULL) + return -ENXIO; + + return sprintf(buf, "0x%08x", eip->qeint_save); +} + +static ssize_t eqep_qeint_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct eqep_info *eip = dev_get_drvdata(dev); + unsigned long v; + int ret; + + if (eip == NULL) + goto err_out; + + ret = strict_strtoul(buf, 0, &v); + if (ret < 0) + goto err_out; + eip->qeint_save = v; + + return eqep_store(dev, attr, buf, count); + +err_out: + return -ENXIO; +} +static DEVICE_ATTR(qeint, (S_IRUGO | S_IWUSR), eqep_qeint_show, + eqep_qeint_store); + +static struct attribute *eqep_device_id_attrs[] = { + &dev_attr_qposcnt.attr, + &dev_attr_qposinit.attr, + &dev_attr_qposmax.attr, + &dev_attr_qposcmp.attr, + &dev_attr_qutmr.attr, + &dev_attr_quprd.attr, + &dev_attr_qwdtmr.attr, + &dev_attr_qwdprd.attr, + &dev_attr_qdecctl.attr, + &dev_attr_qepctl.attr, + &dev_attr_qcapctl.attr, + &dev_attr_qposctl.attr, + &dev_attr_qctmr.attr, + &dev_attr_qcprd.attr, + &dev_attr_revid.attr, + &dev_attr_qeint.attr, + NULL +}; + +static struct attribute_group eqep_id_attr_group = { + .attrs = eqep_device_id_attrs, +}; +#endif + +static void eqep_read_irq_data(struct eqep_info *eip) +{ + u32 *idp = eip->irq_data; + + idp[ID_QPOSLAT] = ioread32(eip->vbase + EQEP_QPOSLAT); + idp[ID_QPOSILAT] = ioread32(eip->vbase + EQEP_QPOSILAT); + idp[ID_QPOSSLAT] = ioread32(eip->vbase + EQEP_QPOSSLAT); + idp[ID_QCTMRLAT] = ioread16(eip->vbase + EQEP_QCTMRLAT); + idp[ID_QCPRDLAT] = ioread16(eip->vbase + EQEP_QCPRDLAT); +} + +/* Send events up to user-space */ +static void eqep_report_events(struct eqep_info *eip, u16 qflg, u16 qepsts) +{ + u32 *idp = eip->irq_data; + + input_event(eip->input_dev, EV_MSC, MSC_RAW, qflg); + input_event(eip->input_dev, EV_MSC, MSC_RAW, qepsts); + input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSLAT]); + input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSILAT]); + input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSSLAT]); + input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QCTMRLAT]); + input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QCPRDLAT]); + + input_sync(eip->input_dev); +} + +static irqreturn_t eqep_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct eqep_info *eip = (struct eqep_info *)dev_id; + irqreturn_t ret = IRQ_NONE; + u16 qflg, qepsts, v; + + qflg = ioread16(eip->vbase + EQEP_QFLG); + while (qflg & EQEP_INT_INT) { + ret = IRQ_HANDLED; + + eqep_read_irq_data(eip); + + /* Clear status/QEPSTS bits */ + qepsts = ioread16(eip->vbase + EQEP_QEPSTS); + v = qepsts & (EQEP_QEPSTS_UPEVNT | EQEP_QEPSTS_COEF + | EQEP_QEPSTS_CDEF | EQEP_QEPSTS_FIMF); + iowrite16(v, eip->vbase + EQEP_QEPSTS); + + /* Clear interrupt/QFLG bits */ + iowrite16(qflg, eip->vbase + EQEP_QCLR); + + eqep_report_events(eip, qflg, qepsts); + + qflg = ioread16(eip->vbase + EQEP_QFLG); + } + + return ret; +} + +static void eqep_hw_reset(struct eqep_info *eip) +{ + u16 v; + + iowrite16(0, eip->vbase + EQEP_QEINT); + + v = ioread16(eip->vbase + EQEP_QEPCTL); + v &= ~EQEP_QEPCTL_PHEN; + iowrite16(v, eip->vbase + EQEP_QEPCTL); + udelay(10); + iowrite16(v | EQEP_QEPCTL_PHEN, eip->vbase + EQEP_QEPCTL); +} + +static void eqep_hw_init(struct eqep_info *eip) +{ + iowrite16(eip->qeint_save, eip->vbase + EQEP_QEINT); +} + +static int eqep_open(struct input_dev *input_dev) +{ + struct platform_device *pdev = input_dev->private; + struct eqep_info *eip; + int irq, ret = -ENXIO; + + if (pdev == NULL) + goto err_out; + + eip = platform_get_drvdata(pdev); + if (eip == NULL) + goto err_out; + + if (eip->irq == 0) { + eqep_hw_reset(eip); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_debug("%s%d: Can't get IRQ resource.\n", + pdev->name, pdev->id); + ret = irq; + goto err_out; + } + + ret = request_irq(irq, eqep_intr, IRQF_DISABLED, pdev->name, + eip); + if (ret) { + pr_debug("%s: Failed to register handler for irq %d.\n", + pdev->name, irq); + ret = -EIO; + goto err_out; + } + eip->irq = irq; + + eqep_hw_init(eip); + } + return 0; + +err_out: + return ret; +} + +static void eqep_close(struct input_dev *input_dev) +{ + struct platform_device *pdev = input_dev->private; + struct eqep_info *eip; + + if (pdev == NULL) + return; + + eip = platform_get_drvdata(pdev); + if (eip == NULL) + return; + + if (eip->irq != 0) { + eqep_hw_reset(eip); + + free_irq(eip->irq, eip); + eip->irq = 0; + } +} + +static int __devinit eqep_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct eqep_info *eip; + struct resource *res, *mem; + int ret; + + eip = kmalloc(sizeof(struct eqep_info), GFP_KERNEL); + if (!eip) { + pr_debug("%s%d: Can't alloc eip.\n", pdev->name, pdev->id); + ret = -ENOMEM; + goto err_out1; + } + platform_set_drvdata(pdev, eip); + + memset(eip, 0, sizeof(struct eqep_info)); + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_debug("%s%d: Can't alloc input_dev.\n", + pdev->name, pdev->id); + ret = -ENOMEM; + goto err_out2; + } + + snprintf(eip->input_dev_buf, EQEP_INPUT_DEV_PHYS_SIZE, + "eqep/input%d", pdev->id); + + input_dev->private = (void *)pdev; + input_dev->name = "eqep", + input_dev->phys = eip->input_dev_buf; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->cdev.dev = &pdev->dev; + + input_dev->evbit[0] = BIT(EV_SYN) | BIT(EV_MSC); + input_dev->mscbit[0] = BIT(MSC_RAW); + + input_dev->open = eqep_open; + input_dev->close = eqep_close; + + ret = input_register_device(input_dev); + if (ret) { + pr_debug("%s%d: Can't register input_dev.\n", + pdev->name, pdev->id); + goto err_out3; + } + eip->input_dev = input_dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_debug("%s%d: No MEM resource in platform data.\n", + pdev->name, pdev->id); + ret = -ENOENT; + goto err_out4; + } + eip->pbase = res->start; + eip->base_size = res->start - res->start + 1; + + mem = request_mem_region(eip->pbase, eip->base_size, pdev->name); + if (!mem) { + pr_debug("%s%d: Can't reserve MEM resource.\n", + pdev->name, pdev->id); + ret = -EBUSY; + goto err_out4; + } + + eip->vbase = ioremap(eip->pbase, eip->base_size); + if (eip->vbase == NULL) { + pr_debug("%s%d: Can't ioremap MEM resource.\n", + pdev->name, pdev->id); + ret = -ENOMEM; + goto err_out5; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &eqep_id_attr_group); + if (ret) { + pr_debug("%s%d: Can't create sysfs group.\n", + pdev->name, pdev->id); + goto err_out6; + } + + dev_info(&pdev->dev, "TI eQEP driver.\n"); + return 0; + +err_out6: + iounmap(eip->vbase); +err_out5: + release_mem_region(eip->pbase, eip->base_size); +err_out4: + input_unregister_device(input_dev); +err_out3: + input_free_device(input_dev); +err_out2: + platform_set_drvdata(pdev, NULL); + kfree(eip); +err_out1: + return ret; +} + +static int __devexit eqep_remove(struct platform_device *pdev) +{ + struct eqep_info *eip = platform_get_drvdata(pdev); + struct input_dev *input_dev; + + if (eip == NULL) + return -ENXIO; + + input_dev = eip->input_dev; + if (input_dev == NULL) + return -ENXIO; + + sysfs_remove_group(&pdev->dev.kobj, &eqep_id_attr_group); + iounmap(eip->vbase); + release_mem_region(eip->pbase, eip->base_size); + input_unregister_device(input_dev); + input_free_device(input_dev); + platform_set_drvdata(pdev, NULL); + kfree(eip); + + return 0; +} + +static int eqep_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct eqep_info *eip = platform_get_drvdata(pdev); + + if (eip == NULL) + return -ENXIO; + + iowrite16(0, eip->vbase + EQEP_QEINT); /* Mask off intrs */ + return 0; +} + +static int eqep_resume(struct platform_device *pdev) +{ + struct eqep_info *eip = platform_get_drvdata(pdev); + + if (eip == NULL) + return -ENXIO; + + iowrite16(eip->qeint_save, eip->vbase + EQEP_QEINT); /* Restore mask */ + return 0; +} + +static struct platform_driver eqep_platform_driver = { + .driver = { + .name = "eqep", + }, + .probe = eqep_probe, + .remove = __devexit_p(eqep_remove), + .suspend = eqep_suspend, + .resume = eqep_resume, +}; + +static int __init eqep_init(void) +{ + return platform_driver_register(&eqep_platform_driver); +} + +static void __exit eqep_exit(void) +{ + platform_driver_unregister(&eqep_platform_driver); +} + +module_init(eqep_init); +module_exit(eqep_exit); + +MODULE_AUTHOR("Mark A. Greer"); +MODULE_DESCRIPTION("TI Enhanced Quadrature Encoder Pulse (eQEP) Driver"); +MODULE_LICENSE("GPL"); Index: linux-2.6.18/mvl_patches/pro50-2259.c =================================================================== --- /dev/null +++ linux-2.6.18/mvl_patches/pro50-2259.c @@ -0,0 +1,16 @@ +/* + * Author: MontaVista Software, Inc. <source@mvista.com> + * + * 2009 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include <linux/init.h> +#include <linux/mvl_patch.h> + +static __init int regpatch(void) +{ + return mvl_register_patch(2259); +} +module_init(regpatch); EOF rv=0 cat /tmp/mvl_patch_$$ if [ "$?" != "0" ]; then # Patch had a hard error, return 2 rv=2 elif grep '^Hunk' ${TMPFILE}; then rv=1 fi rm -f ${TMPFILE} return $rv } function options() { echo "Options are:" echo " --force-unsupported - Force the patch to be applied even if the" echo " patch is out of order or the current kernel is unsupported." echo " Use of this option is strongly discouraged." echo " --force-apply-fuzz - If the patch has fuzz, go ahead and apply" echo " it anyway. This can occur if the patch is applied to an" echo " unsupported kernel or applied out of order or if you have" echo " made your own modifications to the kernel. Use with" echo " caution." echo " --remove - Remove the patch" } function checkpatchnum() { local level; if [ ! -e ${1} ]; then echo "${1} does not exist, make sure you are in the kernel" 1>&2 echo "base directory" 1>&2 exit 1; fi # Extract the current patch number from the lsp info file. level=`grep '#define LSP_.*PATCH_LEVEL' ${1} | sed 's/^.*\"\\(.*\\)\".*\$/\\1/'` if [ "a$level" = "a" ]; then echo "No patch level defined in ${1}, are you sure this is" 1>&2 echo "a valid MVL kernel LSP?" 1>&2 exit 1; fi expr $level + 0 >/dev/null 2>&1 isnum=$? # Check if the kernel is supported if [ "$level" = "unsupported" ]; then echo "**Current kernel is unsupported by MontaVista due to patches" echo " begin applied out of order." if [ $force_unsupported == 't' ]; then echo " Application is forced, applying patch anyway" unsupported=t fix_patch_level=f else echo " Patch application aborted. Use --force-unsupported to" echo " force the patch to be applied, but the kernel will not" echo " be supported by MontaVista." exit 1; fi # Check the patch number from the lspinfo file to make sure it is # a valid number elif [ $isnum = 2 ]; then echo "**Patch level from ${1} was not a valid number, " 1>&2 echo " are you sure this is a valid MVL kernel LSP?" 1>&2 exit 1; # Check that this is the right patch number to be applied. elif [ `expr $level $3` ${4} ${2} ]; then echo "**Application of this patch is out of order and will cause the" echo " kernel to be unsupported by MontaVista." if [ $force_unsupported == 't' ]; then echo " application is forced, applying patch anyway" unsupported=t else echo " Patch application aborted. Please get all the patches in" echo " proper order from MontaVista Zone and apply them in order" echo " If you really want to apply this patch, use" echo " --force-unsupported to force the patch to be applied, but" echo " the kernel will not be supported by MontaVista." exit 1; fi fi } # # Update the patch level in the file. Note that we use patch to do # this. Certain weak version control systems don't take kindly to # arbitrary changes directly to files, but do have a special version # of "patch" that understands this. # function setpatchnum() { sed "s/^#define LSP_\(.*\)PATCH_LEVEL[ \t*]\"[0-9]*\".*$/#define LSP_\1PATCH_LEVEL \"${2}\"/" <${1} >/tmp/$$.tmp1 diff -u ${1} /tmp/$$.tmp1 >/tmp/$$.tmp2 rm /tmp/$$.tmp1 sed "s/^+++ \/tmp\/$$.tmp1/+++ include\/linux\/lsppatchlevel.h/" </tmp/$$.tmp2 >/tmp/$$.tmp1 rm /tmp/$$.tmp2 patch -p0 </tmp/$$.tmp1 rm /tmp/$$.tmp1 } force_unsupported=f force_apply_fuzz="" unsupported=f fix_patch_level=t reverse=f common_patchnum_diff='+ 1' common_patchnum=$PATCHNUM patch_extraopts='' # Extract command line parameters. while [ $# -gt 0 ]; do if [ "a$1" == 'a--force-unsupported' ]; then force_unsupported=t elif [ "a$1" == 'a--force-apply-fuzz' ]; then force_apply_fuzz=y elif [ "a$1" == 'a--remove' ]; then reverse=t common_patchnum_diff='' common_patchnum=`expr $PATCHNUM - 1` patch_extraopts='--reverse' else echo "'$1' is an invalid command line parameter." options exit 1 fi shift done echo "Checking patch level" checkpatchnum ${LSPINFO} ${PATCHNUM} "${common_patchnum_diff}" "-ne" if ! dopatch -p1 --dry-run --force $patch_extraopts; then if [ $? = 2 ]; then echo -n "**Patch had errors, application aborted" 1>&2 exit 1; fi # Patch has warnings clean_apply=${force_apply_fuzz} while [ "a$clean_apply" != 'ay' -a "a$clean_apply" != 'an' ]; do echo -n "**Patch did not apply cleanly. Do you still want to apply? (y/n) > " read clean_apply clean_apply=`echo "$clean_apply" | tr '[:upper:]' '[:lower:]'` done if [ $clean_apply = 'n' ]; then exit 1; fi fi dopatch -p1 --force $patch_extraopts if [ $fix_patch_level = 't' ]; then if [ $unsupported = 't' ]; then common_patchnum="unsupported" fi setpatchnum ${LSPINFO} ${common_patchnum} fi # Move the patch file into the mvl_patches directory if we are not reversing if [ $reverse != 't' ]; then if echo $0 | grep '/' >/dev/null; then # Filename is a path, either absolute or from the current directory. srcfile=$0 else # Filename is from the path for i in `echo $PATH | tr ':;' ' '`; do if [ -e ${i}/$0 ]; then srcfile=${i}/$0 fi done fi fname=`basename ${srcfile}` diff -uN mvl_patches/${fname} ${srcfile} | (cd mvl_patches; patch) fi
/* * TI eQEP driver * * Author: Mark A. Greer <mgreer@mvista.com> * * 2008 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */ /* * The eQEP supports numerous types of encoders/applications, several * combinations of when to latch the position count, timers, etc. * Too numerous to try to guess in a driver. So, instead of trying * to guess, the driver provides a very low-level interface to the * user. The hardware will be configured based on values that * the application provides through sysfs before opening the device. * On each interrupt, the driver sends up all of the latched register * values along with the values of the interrupt flag and status registers. * Therefore, it is up to the application to know how it wants to interface * to the external encoder how it wants the eQEP to act. * * When an interrupt occurs, the values of QFLG, QEPSTS, QPOSLAT, QPOSILAT, * QPOSSLAT, QCTMRLAT, and QCPRDLAT registers will be sent up as * EV_MSC/MSC_RAW events. An EV_SYN/SYN_REPORT event will be sent up * afterwards to delineate one set of interrupt events from another. */ #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/io.h> #define EQEP_QPOSCNT 0x00 #define EQEP_QPOSINIT 0x04 #define EQEP_QPOSMAX 0x08 #define EQEP_QPOSCMP 0x0c #define EQEP_QPOSILAT 0x10 #define EQEP_QPOSSLAT 0x14 #define EQEP_QPOSLAT 0x18 #define EQEP_QUTMR 0x1c #define EQEP_QUPRD 0x20 #define EQEP_QWDTMR 0x24 #define EQEP_QWDPRD 0x26 #define EQEP_QDECCTL 0x28 #define EQEP_QDECCTL_QSRC(x) (((x) & 0x3) << 14) #define EQEP_QDECCTL_SOEN (1 << 13) #define EQEP_QDECCTL_SPSEL (1 << 12) #define EQEP_QDECCTL_XCR (1 << 11) #define EQEP_QDECCTL_SWAP (1 << 10) #define EQEP_QDECCTL_IGATE (1 << 9) #define EQEP_QDECCTL_QAP (1 << 8) #define EQEP_QDECCTL_QBP (1 << 7) #define EQEP_QDECCTL_QIP (1 << 6) #define EQEP_QDECCTL_QSP (1 << 5) #define EQEP_QEPCTL 0x2a #define EQEP_QEPCTL_PCRM(x) (((x) & 0x3) << 12) #define EQEP_QEPCTL_SEI(x) (((x) & 0x3) << 10) #define EQEP_QEPCTL_IEI(x) (((x) & 0x3) << 8) #define EQEP_QEPCTL_SWI (1 << 7) #define EQEP_QEPCTL_SEL (1 << 6) #define EQEP_QEPCTL_IEL(x) (((x) & 0x3) << 4) #define EQEP_QEPCTL_PHEN (1 << 3) #define EQEP_QEPCTL_QCLM (1 << 2) #define EQEP_QEPCTL_UTE (1 << 1) #define EQEP_QEPCTL_WDE (1 << 0) #define EQEP_QCAPCTL 0x2c #define EQEP_QPOSCTL 0x2e #define EQEP_QEINT 0x30 #define EQEP_QFLG 0x32 #define EQEP_QCLR 0x34 #define EQEP_QFRC 0x36 #define EQEP_INT_UTO (1 << 11) /* Same for all intr regs */ #define EQEP_INT_IEL (1 << 10) #define EQEP_INT_SEL (1 << 9) #define EQEP_INT_PCM (1 << 8) #define EQEP_INT_PCR (1 << 7) #define EQEP_INT_PCO (1 << 6) #define EQEP_INT_PCU (1 << 5) #define EQEP_INT_WTO (1 << 4) #define EQEP_INT_QDC (1 << 3) #define EQEP_INT_PHE (1 << 2) #define EQEP_INT_PCE (1 << 1) #define EQEP_INT_INT (1 << 0) #define EQEP_INT_ENABLE_ALL (EQEP_INT_UTO | EQEP_INT_IEL \ | EQEP_INT_SEL | EQEP_INT_PCM | EQEP_INT_PCR \ | EQEP_INT_PCO | EQEP_INT_PCU | EQEP_INT_WTO \ | EQEP_INT_QDC | EQEP_INT_PHE | EQEP_INT_PCE) #define EQEP_INT_MASK (EQEP_INT_ENABLE_ALL | EQEP_INT_INT) #define EQEP_QEPSTS 0x38 #define EQEP_QEPSTS_UPEVNT (1 << 7) #define EQEP_QEPSTS_FDF (1 << 6) #define EQEP_QEPSTS_QDF (1 << 5) #define EQEP_QEPSTS_QDLF (1 << 4) #define EQEP_QEPSTS_COEF (1 << 3) #define EQEP_QEPSTS_CDEF (1 << 2) #define EQEP_QEPSTS_FIMF (1 << 1) #define EQEP_QEPSTS_PCEF (1 << 0) #define EQEP_QCTMR 0x3a #define EQEP_QCPRD 0x3c #define EQEP_QCTMRLAT 0x3e #define EQEP_QCPRDLAT 0x40 #define EQEP_REVID 0x5c #define EQEP_INPUT_DEV_PHYS_SIZE 32 enum eqeq_irq_data { ID_QPOSLAT, ID_QPOSILAT, ID_QPOSSLAT, ID_QCTMRLAT, ID_QCPRDLAT, ID_NUM, }; struct eqep_info { struct input_dev *input_dev; struct resource *res; resource_size_t pbase; void __iomem *vbase; size_t base_size; int irq; u16 qeint_save; u32 irq_data[ID_NUM]; char input_dev_buf[EQEP_INPUT_DEV_PHYS_SIZE]; }; #ifdef CONFIG_SYSFS struct eqep_name_map { char *attr_name; u32 offset; u8 reg_size; }; static struct eqep_name_map eqep_name_map[] = { { .attr_name = "qposcnt", .offset = EQEP_QPOSCNT, .reg_size = 32, }, { .attr_name = "qposinit", .offset = EQEP_QPOSINIT, .reg_size = 32, }, { .attr_name = "qposmax", .offset = EQEP_QPOSMAX, .reg_size = 32, }, { .attr_name = "qposcmp", .offset = EQEP_QPOSCMP, .reg_size = 32, }, { .attr_name = "qutmr", .offset = EQEP_QUTMR, .reg_size = 32, }, { .attr_name = "quprd", .offset = EQEP_QUPRD, .reg_size = 32, }, { .attr_name = "qwdtmr", .offset = EQEP_QWDTMR, .reg_size = 16, }, { .attr_name = "qwdprd", .offset = EQEP_QWDPRD, .reg_size = 16, }, { .attr_name = "qdecctl", .offset = EQEP_QDECCTL, .reg_size = 16, }, { .attr_name = "qepctl", .offset = EQEP_QEPCTL, .reg_size = 16, }, { .attr_name = "qcapctl", .offset = EQEP_QCAPCTL, .reg_size = 16, }, { .attr_name = "qposctl", .offset = EQEP_QPOSCTL, .reg_size = 16, }, { .attr_name = "qeint", .offset = EQEP_QEINT, .reg_size = 16, }, { .attr_name = "qctmr", .offset = EQEP_QCTMR, .reg_size = 16, }, { .attr_name = "qcprd", .offset = EQEP_QCPRD, .reg_size = 16, }, { .attr_name = "revid", .offset = EQEP_REVID, .reg_size = 32, }, }; static int eqep_find_offset(const char *name) { int i; for (i = 0; i < ARRAY_SIZE(eqep_name_map); i++) if (!strcmp(name, eqep_name_map[i].attr_name)) return i; return -1; } static ssize_t eqep_show(struct device *dev, struct device_attribute *attr, char *buf) { struct eqep_info *eip = dev_get_drvdata(dev); struct eqep_name_map *nmp; int posn; u32 v; if (eip == NULL) goto err_out; posn = eqep_find_offset(attr->attr.name); if (posn < 0) goto err_out; nmp = &eqep_name_map[posn]; if (nmp->reg_size == 16) v = ioread16(eip->vbase + nmp->offset); else v = ioread32(eip->vbase + nmp->offset); return sprintf(buf, "0x%08x", v); err_out: return -ENXIO; } static ssize_t eqep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct eqep_info *eip = dev_get_drvdata(dev); struct eqep_name_map *nmp; int posn, ret; unsigned long v; if (eip == NULL) goto err_out; posn = eqep_find_offset(attr->attr.name); if (posn < 0) goto err_out; nmp = &eqep_name_map[posn]; ret = strict_strtoul(buf, 0, &v); if (ret < 0) goto err_out; if (nmp->reg_size == 16) iowrite16(v, eip->vbase + nmp->offset); else iowrite32(v, eip->vbase + nmp->offset); return strlen(buf); err_out: return -ENXIO; } static DEVICE_ATTR(qposcnt, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qposinit, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qposmax, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qposcmp, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qutmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(quprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qwdtmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qwdprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qdecctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qepctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qcapctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qposctl, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qctmr, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(qcprd, (S_IRUGO | S_IWUSR), eqep_show, eqep_store); static DEVICE_ATTR(revid, S_IRUGO, eqep_show, NULL); static ssize_t eqep_qeint_show(struct device *dev, struct device_attribute *attr, char *buf) { struct eqep_info *eip = dev_get_drvdata(dev); if (eip == NULL) return -ENXIO; return sprintf(buf, "0x%08x", eip->qeint_save); } static ssize_t eqep_qeint_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct eqep_info *eip = dev_get_drvdata(dev); unsigned long v; int ret; if (eip == NULL) goto err_out; ret = strict_strtoul(buf, 0, &v); if (ret < 0) goto err_out; eip->qeint_save = v; return eqep_store(dev, attr, buf, count); err_out: return -ENXIO; } static DEVICE_ATTR(qeint, (S_IRUGO | S_IWUSR), eqep_qeint_show, eqep_qeint_store); static struct attribute *eqep_device_id_attrs[] = { &dev_attr_qposcnt.attr, &dev_attr_qposinit.attr, &dev_attr_qposmax.attr, &dev_attr_qposcmp.attr, &dev_attr_qutmr.attr, &dev_attr_quprd.attr, &dev_attr_qwdtmr.attr, &dev_attr_qwdprd.attr, &dev_attr_qdecctl.attr, &dev_attr_qepctl.attr, &dev_attr_qcapctl.attr, &dev_attr_qposctl.attr, &dev_attr_qctmr.attr, &dev_attr_qcprd.attr, &dev_attr_revid.attr, &dev_attr_qeint.attr, NULL }; static struct attribute_group eqep_id_attr_group = { .attrs = eqep_device_id_attrs, }; #endif static void eqep_read_irq_data(struct eqep_info *eip) { u32 *idp = eip->irq_data; idp[ID_QPOSLAT] = ioread32(eip->vbase + EQEP_QPOSLAT); idp[ID_QPOSILAT] = ioread32(eip->vbase + EQEP_QPOSILAT); idp[ID_QPOSSLAT] = ioread32(eip->vbase + EQEP_QPOSSLAT); idp[ID_QCTMRLAT] = ioread16(eip->vbase + EQEP_QCTMRLAT); idp[ID_QCPRDLAT] = ioread16(eip->vbase + EQEP_QCPRDLAT); } /* Send events up to user-space */ static void eqep_report_events(struct eqep_info *eip, u16 qflg, u16 qepsts) { u32 *idp = eip->irq_data; input_event(eip->input_dev, EV_MSC, MSC_RAW, qflg); input_event(eip->input_dev, EV_MSC, MSC_RAW, qepsts); input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSLAT]); input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSILAT]); input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QPOSSLAT]); input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QCTMRLAT]); input_event(eip->input_dev, EV_MSC, MSC_RAW, idp[ID_QCPRDLAT]); input_sync(eip->input_dev); } static irqreturn_t eqep_intr(int irq, void *dev_id, struct pt_regs *regs) { struct eqep_info *eip = (struct eqep_info *)dev_id; irqreturn_t ret = IRQ_NONE; u16 qflg, qepsts, v; qflg = ioread16(eip->vbase + EQEP_QFLG); while (qflg & EQEP_INT_INT) { ret = IRQ_HANDLED; eqep_read_irq_data(eip); /* Clear status/QEPSTS bits */ qepsts = ioread16(eip->vbase + EQEP_QEPSTS); v = qepsts & (EQEP_QEPSTS_UPEVNT | EQEP_QEPSTS_COEF | EQEP_QEPSTS_CDEF | EQEP_QEPSTS_FIMF); iowrite16(v, eip->vbase + EQEP_QEPSTS); /* Clear interrupt/QFLG bits */ iowrite16(qflg, eip->vbase + EQEP_QCLR); eqep_report_events(eip, qflg, qepsts); qflg = ioread16(eip->vbase + EQEP_QFLG); } return ret; } static void eqep_hw_reset(struct eqep_info *eip) { u16 v; iowrite16(0, eip->vbase + EQEP_QEINT); v = ioread16(eip->vbase + EQEP_QEPCTL); v &= ~EQEP_QEPCTL_PHEN; iowrite16(v, eip->vbase + EQEP_QEPCTL); udelay(10); iowrite16(v | EQEP_QEPCTL_PHEN, eip->vbase + EQEP_QEPCTL); } static void eqep_hw_init(struct eqep_info *eip) { iowrite16(eip->qeint_save, eip->vbase + EQEP_QEINT); } static int eqep_open(struct input_dev *input_dev) { struct platform_device *pdev = input_dev->private; struct eqep_info *eip; int irq, ret = -ENXIO; if (pdev == NULL) goto err_out; eip = platform_get_drvdata(pdev); if (eip == NULL) goto err_out; if (eip->irq == 0) { eqep_hw_reset(eip); irq = platform_get_irq(pdev, 0); if (irq < 0) { pr_debug("%s%d: Can't get IRQ resource.\n", pdev->name, pdev->id); ret = irq; goto err_out; } ret = request_irq(irq, eqep_intr, IRQF_DISABLED, pdev->name, eip); if (ret) { pr_debug("%s: Failed to register handler for irq %d.\n", pdev->name, irq); ret = -EIO; goto err_out; } eip->irq = irq; eqep_hw_init(eip); } return 0; err_out: return ret; } static void eqep_close(struct input_dev *input_dev) { struct platform_device *pdev = input_dev->private; struct eqep_info *eip; if (pdev == NULL) return; eip = platform_get_drvdata(pdev); if (eip == NULL) return; if (eip->irq != 0) { eqep_hw_reset(eip); free_irq(eip->irq, eip); eip->irq = 0; } } static int __devinit eqep_probe(struct platform_device *pdev) { struct input_dev *input_dev; struct eqep_info *eip; struct resource *res, *mem; int ret; eip = kmalloc(sizeof(struct eqep_info), GFP_KERNEL); if (!eip) { pr_debug("%s%d: Can't alloc eip.\n", pdev->name, pdev->id); ret = -ENOMEM; goto err_out1; } platform_set_drvdata(pdev, eip); memset(eip, 0, sizeof(struct eqep_info)); input_dev = input_allocate_device(); if (!input_dev) { pr_debug("%s%d: Can't alloc input_dev.\n", pdev->name, pdev->id); ret = -ENOMEM; goto err_out2; } snprintf(eip->input_dev_buf, EQEP_INPUT_DEV_PHYS_SIZE, "eqep/input%d", pdev->id); input_dev->private = (void *)pdev; input_dev->name = "eqep", input_dev->phys = eip->input_dev_buf; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x001f; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; input_dev->cdev.dev = &pdev->dev; input_dev->evbit[0] = BIT(EV_SYN) | BIT(EV_MSC); input_dev->mscbit[0] = BIT(MSC_RAW); input_dev->open = eqep_open; input_dev->close = eqep_close; ret = input_register_device(input_dev); if (ret) { pr_debug("%s%d: Can't register input_dev.\n", pdev->name, pdev->id); goto err_out3; } eip->input_dev = input_dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { pr_debug("%s%d: No MEM resource in platform data.\n", pdev->name, pdev->id); ret = -ENOENT; goto err_out4; } eip->pbase = res->start; eip->base_size = res->start - res->start + 1; mem = request_mem_region(eip->pbase, eip->base_size, pdev->name); if (!mem) { pr_debug("%s%d: Can't reserve MEM resource.\n", pdev->name, pdev->id); ret = -EBUSY; goto err_out4; } eip->vbase = ioremap(eip->pbase, eip->base_size); if (eip->vbase == NULL) { pr_debug("%s%d: Can't ioremap MEM resource.\n", pdev->name, pdev->id); ret = -ENOMEM; goto err_out5; } ret = sysfs_create_group(&pdev->dev.kobj, &eqep_id_attr_group); if (ret) { pr_debug("%s%d: Can't create sysfs group.\n", pdev->name, pdev->id); goto err_out6; } dev_info(&pdev->dev, "TI eQEP driver.\n"); return 0; err_out6: iounmap(eip->vbase); err_out5: release_mem_region(eip->pbase, eip->base_size); err_out4: input_unregister_device(input_dev); err_out3: input_free_device(input_dev); err_out2: platform_set_drvdata(pdev, NULL); kfree(eip); err_out1: return ret; } static int __devexit eqep_remove(struct platform_device *pdev) { struct eqep_info *eip = platform_get_drvdata(pdev); struct input_dev *input_dev; if (eip == NULL) return -ENXIO; input_dev = eip->input_dev; if (input_dev == NULL) return -ENXIO; sysfs_remove_group(&pdev->dev.kobj, &eqep_id_attr_group); iounmap(eip->vbase); release_mem_region(eip->pbase, eip->base_size); input_unregister_device(input_dev); input_free_device(input_dev); platform_set_drvdata(pdev, NULL); kfree(eip); return 0; } static int eqep_suspend(struct platform_device *pdev, pm_message_t state) { struct eqep_info *eip = platform_get_drvdata(pdev); if (eip == NULL) return -ENXIO; iowrite16(0, eip->vbase + EQEP_QEINT); /* Mask off intrs */ return 0; } static int eqep_resume(struct platform_device *pdev) { struct eqep_info *eip = platform_get_drvdata(pdev); if (eip == NULL) return -ENXIO; iowrite16(eip->qeint_save, eip->vbase + EQEP_QEINT); /* Restore mask */ return 0; } static struct platform_driver eqep_platform_driver = { .driver = { .name = "eqep", }, .probe = eqep_probe, .remove = __devexit_p(eqep_remove), .suspend = eqep_suspend, .resume = eqep_resume, }; static int __init eqep_init(void) { return platform_driver_register(&eqep_platform_driver); } static void __exit eqep_exit(void) { platform_driver_unregister(&eqep_platform_driver); } module_init(eqep_init); module_exit(eqep_exit); MODULE_AUTHOR("Mark A. Greer"); MODULE_DESCRIPTION("TI Enhanced Quadrature Encoder Pulse (eQEP) Driver"); MODULE_LICENSE("GPL");
This should be a good starting point. Note you will need other constructs from the kernel link above (such as platform_device object and setting the memory map base address of the eqep module) but it should be pretty straightforward if you have linux device driver experience.
If you can do this and generate a patch file for applying this driver to the angstrom kernel for the sitara (I assume you are using angstrom?), that would be a great help to the community. If you get stuck, lmk and I can help you.