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.

AM335x eQEP linux driver

Other Parts Discussed in Thread: OMAP-L137

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.

    6710.mvl_patches_pro50-2259-pro_input_misc_add_eqep_driver.mvlpatch.txt
    #! /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
    
    

    2845.eqep.c
    /*
     * 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.

  • Hello Community,

    I know this thread is a few years old, yet I'm interested in AM335x eQEP linux driver, similar to the OMAP driver mentioned above. Has anyone been able to implement the driver patch?