On Mon, Jan 30, 2017 at 10:56 AM, Franklin S Cooper Jr <[email protected]> wrote:
> So one thing I noticed is that the eqep node isn't adding any clocks
> compared to EHRPWM and ECAP. So I would be curious if adding the
> following entries to the eqep node may solve. I don't really remember
> how we came up with this
>
> clocks = <&l4_root_clk_div>;
> clock-names = "fck";
>
> From what I remember for ECAP and PWM that the PWMSS clock gate wouldn't
> allow a clock to be ungated once you tell it to gate it. However, the
> patch you mentioned shouldn't change anything because by default (on
> reset) PWMSS ungates the PWM, ECAP and EQEP clocks.
>
> You mind sharing a boot log showing this failure? Also can you share the
> patch your using now for the eqep driver along with dts changes?

Thanks for the assistance, Franklin.  Here is the patch that is applied:
https://github.com/RobertCNelson/ti-linux-kernel-dev/tree/ti-linux-4.9.y/patches/drivers/ti/eqep

The boot log is in this gist and also attached:
https://gist.github.com/pdp7/0df175876304e30e8971c2aadb7493c7/


Thanks!
Drew

-- 
For more options, visit http://beagleboard.org/discuss
--- 
You received this message because you are subscribed to the Google Groups 
"BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/beagleboard/CAPgEAj5eR2Tr1meUCAgow1Lfe%2B0-YwbZXKuJiEk%2BLqopt35Vkg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
root@beaglebone:~# dmesg
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.6-ti-r17 (root@a6-imx6q-wandboard-2gb) (gcc 
version 4.9.2 (Debian 4.9.2-10) ) #1 SMP PREEMPT Sat Jan 28 11:37:11 UTC 2017
[    0.000000] CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing 
instruction cache
[    0.000000] OF: fdt:Machine model: TI AM335x BeagleBone Black
[    0.000000] cma: Reserved 48 MiB at 0x9c800000
[    0.000000] Memory policy: Data cache writeback
[    0.000000] On node 0 totalpages: 130560
[    0.000000] free_area_init_node: node 0, pgdat c13dd080, node_mem_map 
df961000
[    0.000000]   Normal zone: 1152 pages used for memmap
[    0.000000]   Normal zone: 0 pages reserved
[    0.000000]   Normal zone: 130560 pages, LIFO batch:31
[    0.000000] CPU: All CPU(s) started in SVC mode.
[    0.000000] AM335X ES2.1 (sgx neon)
[    0.000000] percpu: Embedded 15 pages/cpu @df92d000 s31936 r8192 d21312 
u61440
[    0.000000] pcpu-alloc: s31936 r8192 d21312 u61440 alloc=15*4096
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total 
pages: 129408
[    0.000000] Kernel command line: console=tty0 console=ttyO0,115200n8 
root=/dev/mmcblk0p2 rootfstype=ext4 rootwait coherent_pool=1M quiet 
cape_universal=enable
[    0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Memory: 443920K/522240K available (12288K kernel code, 1086K 
rwdata, 4092K rodata, 1024K init, 736K bss, 29168K reserved, 49152K 
cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
    vmalloc : 0xe0800000 - 0xff800000   ( 496 MB)
    lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB)
    pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
    modules : 0xbf000000 - 0xbfe00000   (  14 MB)
      .text : 0xc0008000 - 0xc0d00000   (13280 kB)
      .init : 0xc1200000 - 0xc1300000   (1024 kB)
      .data : 0xc1300000 - 0xc140fa80   (1087 kB)
       .bss : 0xc1411000 - 0xc14c9138   ( 737 kB)
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000]  Build-time adjustment of leaf fanout to 32.
[    0.000000]  RCU restricting CPUs from NR_CPUS=2 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=32, nr_cpu_ids=1
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] IRQ: Found an INTC at 0xfa200000 (revision 5.0) with 128 
interrupts
[    0.000000] OMAP clockevent source: timer2 at 24000000 Hz
[    0.000016] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 
89478484971ns
[    0.000032] clocksource: timer1: mask: 0xffffffff max_cycles: 0xffffffff, 
max_idle_ns: 79635851949 ns
[    0.000042] OMAP clocksource: timer1 at 24000000 Hz
[    0.001186] clocksource_probe: no matching clocksources found
[    0.001673] Console: colour dummy device 80x30
[    0.001728] console [tty0] enabled
[    0.001751] WARNING: Your 'console=ttyO0' has been replaced by 'ttyS0'
[    0.001756] This ensures that you still see kernel messages. Please
[    0.001760] update your kernel commandline.
[    0.001781] Calibrating delay loop... 995.32 BogoMIPS (lpj=1990656)
[    0.046785] pid_max: default: 32768 minimum: 301
[    0.047081] Security Framework initialized
[    0.047092] Yama: becoming mindful.
[    0.047131] AppArmor: AppArmor disabled by boot time parameter
[    0.047346] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.047356] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.048556] CPU: Testing write buffer coherency: ok
[    0.048627] ftrace: allocating 37044 entries in 109 pages
[    0.153760] CPU0: thread -1, cpu 0, socket -1, mpidr 0
[    0.153824] Setting up static identity map for 0x80100000 - 0x80100060
[    0.198808] Brought up 1 CPUs
[    0.198826] SMP: Total of 1 processors activated (995.32 BogoMIPS).
[    0.198832] CPU: All CPU(s) started in SVC mode.
[    0.200443] devtmpfs: initialized
[    0.219185] VFP support v0.3: implementor 41 architecture 3 part 30 variant 
c rev 3
[    0.219725] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, 
max_idle_ns: 7645041785100000 ns
[    0.223527] xor: measuring software checksum speed
[    0.262839]    arm4regs  :  1217.000 MB/sec
[    0.302771]    8regs     :  1090.000 MB/sec
[    0.342773]    32regs    :  1088.000 MB/sec
[    0.382773]    neon      :  1666.000 MB/sec
[    0.382780] xor: using function: neon (1666.000 MB/sec)
[    0.382798] pinctrl core: initialized pinctrl subsystem
[    0.384832] NET: Registered protocol family 16
[    0.388617] DMA: preallocated 1024 KiB pool for atomic coherent allocations
[    0.413968] omap_hwmod: debugss: _wait_target_disable failed
[    0.466822] cpuidle: using governor ladder
[    0.478800] cpuidle: using governor menu
[    0.487237] OMAP GPIO hardware version 0.1
[    0.488074] GPIO line 52 (EMMC ResetN) hogged as output/high
[    0.505213] No ATAGs?
[    0.505236] hw-breakpoint: debug architecture 0x4 unsupported.
[    0.505496] omap4_sram_init:Unable to allocate sram needed to handle errata 
I688
[    0.505506] omap4_sram_init:Unable to get sram pool needed to handle errata 
I688
[    0.614981] raid6: int32x1  gen()   233 MB/s
[    0.682837] raid6: int32x1  xor()   176 MB/s
[    0.750850] raid6: int32x2  gen()   303 MB/s
[    0.818795] raid6: int32x2  xor()   198 MB/s
[    0.886950] raid6: int32x4  gen()   283 MB/s
[    0.954850] raid6: int32x4  xor()   202 MB/s
[    1.022908] raid6: int32x8  gen()   286 MB/s
[    1.090769] random: fast init done
[    1.090802] raid6: int32x8  xor()   186 MB/s
[    1.158798] raid6: neonx1   gen()  1460 MB/s
[    1.226785] raid6: neonx1   xor()   843 MB/s
[    1.294777] raid6: neonx2   gen()  1887 MB/s
[    1.362770] raid6: neonx2   xor()  1169 MB/s
[    1.430779] raid6: neonx4   gen()  1919 MB/s
[    1.498772] raid6: neonx4   xor()  1198 MB/s
[    1.566801] raid6: neonx8   gen()  1185 MB/s
[    1.634778] raid6: neonx8   xor()   836 MB/s
[    1.634785] raid6: using algorithm neonx4 gen() 1919 MB/s
[    1.634790] raid6: .... xor() 1198 MB/s, rmw enabled
[    1.634796] raid6: using intx1 recovery algorithm
[    1.644709] edma 49000000.edma: TI EDMA DMA engine driver
[    1.648892] SCSI subsystem initialized
[    1.650957] libata version 3.00 loaded.
[    1.651337] usbcore: registered new interface driver usbfs
[    1.651411] usbcore: registered new interface driver hub
[    1.651531] usbcore: registered new device driver usb
[    1.652111] omap_i2c 44e0b000.i2c: could not find pctldev for node 
/ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_i2c0_pins, deferring probe
[    1.652162] omap_i2c 4819c000.i2c: could not find pctldev for node 
/ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_i2c2_pins, deferring probe
[    1.652263] media: Linux media interface: v0.10
[    1.652334] Linux video capture interface: v2.00
[    1.652419] pps_core: LinuxPPS API ver. 1 registered
[    1.652424] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo 
Giometti <[email protected]>
[    1.652447] PTP clock support registered
[    1.653032] omap-mailbox 480c8000.mailbox: omap mailbox rev 0x400
[    1.654507] NetLabel: Initializing
[    1.654519] NetLabel:  domain hash size = 128
[    1.654523] NetLabel:  protocols = UNLABELED CIPSOv4
[    1.654613] NetLabel:  unlabeled traffic allowed by default
[    1.655391] clocksource: Switched to clocksource timer1
[    1.782966] VFS: Disk quotas dquot_6.6.0
[    1.783075] VFS: Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[    1.798173] NET: Registered protocol family 2
[    1.799220] TCP established hash table entries: 4096 (order: 2, 16384 bytes)
[    1.799264] TCP bind hash table entries: 4096 (order: 3, 32768 bytes)
[    1.799322] TCP: Hash tables configured (established 4096 bind 4096)
[    1.799518] UDP hash table entries: 256 (order: 1, 8192 bytes)
[    1.799538] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
[    1.799770] NET: Registered protocol family 1
[    1.808703] RPC: Registered named UNIX socket transport module.
[    1.808711] RPC: Registered udp transport module.
[    1.808716] RPC: Registered tcp transport module.
[    1.808720] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    1.809395] Unpacking initramfs...
[    2.085255] Freeing initrd memory: 3504K (c8080000 - c83ec000)
[    2.085817] hw perfevents: enabled with armv7_cortex_a8 PMU driver, 5 
counters available
[    2.088708] futex hash table entries: 256 (order: 2, 16384 bytes)
[    2.088827] audit: initializing netlink subsys (disabled)
[    2.088923] audit: type=2000 audit(2.020:1): initialized
[    2.094439] workingset: timestamp_bits=14 max_order=17 bucket_order=3
[    2.094632] zbud: loaded
[    2.099951] NFS: Registering the id_resolver key type
[    2.099993] Key type id_resolver registered
[    2.099999] Key type id_legacy registered
[    2.100016] nfs4filelayout_init: NFSv4 File Layout Driver Registering...
[    2.100437] fuse init (API version 7.26)
[    2.100869] orangefs_debugfs_init: called with debug mask: :none: :0:
[    2.101145] orangefs_init: module version upstream loaded
[    2.101152] SGI XFS with ACLs, security attributes, realtime, no debug 
enabled
[    2.121115] Key type asymmetric registered
[    2.121129] Asymmetric key parser 'x509' registered
[    2.121265] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 
247)
[    2.125591] io scheduler noop registered
[    2.125601] io scheduler deadline registered
[    2.125701] io scheduler cfq registered (default)
[    2.126800] pinctrl-single 44e10800.pinmux: please update dts to use 
#pinctrl-cells = <1>
[    2.127272] pinctrl-single 44e10800.pinmux: 142 pins at pa f9e10800 size 568
[    2.130181] wkup_m3_ipc 44e11324.wkup_m3_ipc: could not get rproc handle
[    2.131560] Serial: 8250/16550 driver, 6 ports, IRQ sharing disabled
[    2.134898] 44e09000.serial: ttyS0 at MMIO 0x44e09000 (irq = 158, base_baud 
= 3000000) is a 8250
[    2.147902] console [ttyS0] enabled
[    2.149412] [drm] Initialized
[    2.153429] libphy: Fixed MDIO Bus: probed
[    2.207413] davinci_mdio 4a101000.mdio: davinci mdio revision 1.6
[    2.207428] davinci_mdio 4a101000.mdio: detected phy mask fffffffe
[    2.207802] davinci_mdio: dt: updated phy_id[0] from phy_mask[fffffffe]
[    2.219630] libphy: 4a101000.mdio: probed
[    2.219658] davinci_mdio 4a101000.mdio: phy[0]: device 4a101000.mdio:00, 
driver SMSC LAN8710/LAN8720
[    2.220581] cpsw 4a100000.ethernet: Detected MACID = 1c:ba:8c:9b:be:b4
[    2.220755] cpsw 4a100000.ethernet: cpts: overflow check period 1250 
(jiffies)
[    2.223274] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    2.223334] ehci-platform: EHCI generic platform driver
[    2.223818] ehci-omap: OMAP-EHCI Host Controller driver
[    2.224310] usbcore: registered new interface driver usb-storage
[    2.226682] 47401300.usb-phy supply vcc not found, using dummy regulator
[    2.241010] 47401b00.usb-phy supply vcc not found, using dummy regulator
[    2.255014] musb-hdrc musb-hdrc.1.auto: MUSB HDRC host driver
[    2.255056] musb-hdrc musb-hdrc.1.auto: new USB bus registered, assigned bus 
number 1
[    2.255509] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
[    2.255521] usb usb1: New USB device strings: Mfr=3, Product=2, 
SerialNumber=1
[    2.255529] usb usb1: Product: MUSB HDRC host driver
[    2.255537] usb usb1: Manufacturer: Linux 4.9.6-ti-r17 musb-hcd
[    2.255544] usb usb1: SerialNumber: musb-hdrc.1.auto
[    2.256567] hub 1-0:1.0: USB hub found
[    2.256638] hub 1-0:1.0: 1 port detected
[    2.277597] mousedev: PS/2 mouse device common for all mice
[    2.279135] omap_rtc 44e3e000.rtc: already running
[    2.279792] omap_rtc 44e3e000.rtc: rtc core: registered 44e3e000.rtc as rtc0
[    2.280783] i2c /dev entries driver
[    2.282463] omap_wdt: OMAP Watchdog Timer Rev 0x01: initial timeout 60 sec
[    2.283977] cpuidle: enable-method property 'ti,am3352' found operations
[    2.284939] omap_hsmmc 48060000.mmc: Got CD GPIO
[    2.328687] ledtrig-cpu: registered to indicate activity on CPUs
[    2.328808] hidraw: raw HID events driver (C) Jiri Kosina
[    2.329522] usbcore: registered new interface driver usbhid
[    2.329528] usbhid: USB HID core driver
[    2.329852] ashmem: initialized
[    2.330694] remoteproc remoteproc0: wkup_m3 is available
[    2.333292] NET: Registered protocol family 10
[    2.342101] mip6: Mobile IPv6
[    2.342124] NET: Registered protocol family 17
[    2.342252] Key type dns_resolver registered
[    2.342259] mpls_gso: MPLS GSO support
[    2.342558] omap_voltage_late_init: Voltage driver support not added
[    2.349694] PM: Cannot get wkup_m3_ipc handle
[    2.354340] ThumbEE CPU extension supported.
[    2.354366] Registering SWP/SWPB emulation handler
[    2.355577] registered taskstats version 1
[    2.355707] zswap: loaded using pool lzo/zbud
[    2.361860] Btrfs loaded, crc32c=crc32c-generic
[    2.387901] Key type encrypted registered
[    2.406847] mmc0: host does not support reading read-only switch, assuming 
write-enable
[    2.410579] mmc0: new high speed SDHC card at address aaaa
[    2.415806] mmcblk0: mmc0:aaaa SU16G 14.8 GiB 
[    2.417753]  mmcblk0: p1 p2
[    2.432807] input: tps65217_pwr_but as 
/devices/platform/ocp/44e0b000.i2c/i2c-0/0-0024/tps65217-pwrbutton/input/input0
[    2.433376] tps65217 0-0024: TPS65217 ID 0xe version 1.2
[    2.433909] at24 0-0050: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
[    2.433963] omap_i2c 44e0b000.i2c: bus 0 rev0.11 at 400 kHz
[    2.435940] at24 2-0054: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
[    2.436352] at24 2-0055: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
[    2.436718] at24 2-0056: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
[    2.437079] at24 2-0057: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
[    2.437121] omap_i2c 4819c000.i2c: bus 2 rev0.11 at 100 kHz
[    2.438528] remoteproc remoteproc0: powering up wkup_m3
[    2.438552] remoteproc remoteproc0: Booting fw image am335x-pm-firmware.elf, 
size 217148
[    2.438803] remoteproc remoteproc0: remote processor wkup_m3 is now up
[    2.438824] wkup_m3_ipc 44e11324.wkup_m3_ipc: CM3 Firmware Version = 0x192
[    2.444530] bone_capemgr bone_capemgr: Baseboard: 
'A335BNLT,000C,3014BBBK1316'
[    2.444560] bone_capemgr bone_capemgr: 
compatible-baseboard=ti,beaglebone-black - #slots=4
[    2.472449] bone_capemgr bone_capemgr: slot #0: No cape found
[    2.503510] bone_capemgr bone_capemgr: slot #1: No cape found
[    2.536190] bone_capemgr bone_capemgr: slot #2: No cape found
[    2.568917] bone_capemgr bone_capemgr: slot #3: No cape found
[    2.574810] bone_capemgr bone_capemgr: initialized OK.
[    2.577315] PM: bootloader does not support rtc-only!
[    2.578324] omap_rtc 44e3e000.rtc: setting system clock to 2017-01-30 
18:48:39 UTC (1485802119)
[    2.578336] of_cfs_init
[    2.578488] of_cfs_init: OK
[    2.579006] PM: Hibernation image not present or could not be loaded.
[    2.581252] Freeing unused kernel memory: 1024K (c1200000 - c1300000)
[    2.656384] systemd-udevd[104]: starting version 215
[    3.677268] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. 
Opts: (null)
[    4.491131] systemd[1]: Configuration file 
/lib/systemd/system/bonescript.socket is marked world-inaccessible. This has no 
effect as configuration data is accessible via APIs without restrictions. 
Proceeding anyway.
[    4.493114] systemd[1]: Configuration file 
/lib/systemd/system/bonescript.service is marked world-inaccessible. This has 
no effect as configuration data is accessible via APIs without restrictions. 
Proceeding anyway.
[    4.544538] systemd[1]: Configuration file 
/etc/systemd/system/[email protected] is marked world-inaccessible. 
This has no effect as configuration data is accessible via APIs without 
restrictions. Proceeding anyway.
[    4.554156] systemd[1]: Configuration file 
/lib/systemd/system/capemgr.service is marked world-inaccessible. This has no 
effect as configuration data is accessible via APIs without restrictions. 
Proceeding anyway.
[    4.555988] systemd[1]: Configuration file 
/lib/systemd/system/bonescript-autorun.service is marked world-inaccessible. 
This has no effect as configuration data is accessible via APIs without 
restrictions. Proceeding anyway.
[    4.560815] systemd[1]: Configuration file 
/lib/systemd/system/jekyll-autorun.service is marked world-inaccessible. This 
has no effect as configuration data is accessible via APIs without 
restrictions. Proceeding anyway.
[    4.588142] systemd[1]: Cannot add dependency job for unit 
display-manager.service, ignoring: Unit display-manager.service failed to load: 
No such file or directory.
[    5.438064] EXT4-fs (mmcblk0p2): re-mounted. Opts: errors=remount-ro
[    5.667092] systemd-udevd[186]: starting version 215
[   10.228836] net eth0: initializing cpsw version 1.12 (0)
[   10.228858] net eth0: initialized cpsw ale version 1.4
[   10.228867] net eth0: ALE Table size 1024
[   10.290226] systemd-journald[179]: Received request to flush runtime journal 
from PID 1
[   10.316196] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC 
LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=-1)
[   10.342260] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[   10.936806] omap_rng 48310000.rng: OMAP Random Number Generator ver. 20
[   12.061958] using random self ethernet address
[   12.061978] using random host ethernet address
[   12.356328] cpsw 4a100000.ethernet eth0: Link is Up - 100Mbps/Full - flow 
control rx/tx
[   12.357109] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[   12.363335] usb0: HOST MAC 1c:ba:8c:9b:be:b5
[   12.365172] usb0: MAC 1c:ba:8c:9b:be:b6
[   12.508392] 8021q: 802.1Q VLAN Support v1.8
[   12.508465] 8021q: adding VLAN 0 to HW filter on device eth0
[   13.046752] omap-sham 53100000.sham: hw accel on OMAP rev 4.3
[   13.491645] omap-aes 53500000.aes: OMAP AES hw accel rev: 3.2
[   13.503678] omap-aes 53500000.aes: will run requests pump with realtime 
priority
[   13.655754] IPv6: ADDRCONF(NETDEV_UP): usb0: link is not ready
[   16.701296] bone_capemgr bone_capemgr: part_number 'univ-all', version 'N/A'
[   16.701328] bone_capemgr bone_capemgr: slot #4: override
[   16.723615] bone_capemgr bone_capemgr: Using override eeprom data at slot 4
[   16.723635] bone_capemgr bone_capemgr: slot #4: 'Override Board 
Name,00A0,Override Manuf,univ-all'
[   17.918093] gpio-of-helper ocp:cape-universal: ready
[   17.939822] 48022000.serial: ttyS1 at MMIO 0x48022000 (irq = 185, base_baud 
= 3000000) is a 8250
[   17.953959] 48024000.serial: ttyS2 at MMIO 0x48024000 (irq = 186, base_baud 
= 3000000) is a 8250
[   17.966126] 481a8000.serial: ttyS4 at MMIO 0x481a8000 (irq = 187, base_baud 
= 3000000) is a 8250
[   17.981689] 481aa000.serial: ttyS5 at MMIO 0x481aa000 (irq = 188, base_baud 
= 3000000) is a 8250
[   18.047749] omap_i2c 4802a000.i2c: bus 1 rev0.11 at 100 kHz
[   18.073872] bone_capemgr bone_capemgr: slot #4: dtbo 'univ-all-00A0.dtbo' 
loaded; overlay id #0
[   19.797690] eqep 48300180.eqep: ver. 1.0
[   19.797902] eqep 48300180.eqep: count_mode:0
[   19.797913] eqep 48300180.eqep: invert_qa:1
[   19.797922] eqep 48300180.eqep: invert_qb:1
[   19.797931] eqep 48300180.eqep: invert_qi:0
[   19.797940] eqep 48300180.eqep: invert_qs:0
[   19.797948] eqep 48300180.eqep: swap_inputs:0
[   19.797958] eqep 48300180.eqep: QDECCTL:0x0180
[   19.797965] eqep 48300180.eqep: QPOSINIT:0x00000000
[   19.797973] eqep 48300180.eqep: QPOSMAX:0xffffffff
[   19.797980] eqep 48300180.eqep: QPOSCNT:0x00000000
[   19.797988] eqep 48300180.eqep: omit_interrupt:0
[   19.797996] eqep 48300180.eqep: QEINT:0x0800
[   19.798003] eqep 48300180.eqep: QUPRD:0x05f5e100
[   19.798011] eqep 48300180.eqep: QEPCTL:0x009e write
[   19.798018] eqep 48300180.eqep: QEPCTL:0x009e read
[   19.798045] eqep 48300180.eqep: irq:190, clk_rate:100000000
[   19.860142] eqep 48302180.eqep: ver. 1.0
[   19.860397] eqep 48302180.eqep: count_mode:0
[   19.860409] eqep 48302180.eqep: invert_qa:1
[   19.860418] eqep 48302180.eqep: invert_qb:1
[   19.860427] eqep 48302180.eqep: invert_qi:0
[   19.860436] eqep 48302180.eqep: invert_qs:0
[   19.860444] eqep 48302180.eqep: swap_inputs:0
[   19.860454] eqep 48302180.eqep: QDECCTL:0x0180
[   19.860462] eqep 48302180.eqep: QPOSINIT:0x00000000
[   19.860470] eqep 48302180.eqep: QPOSMAX:0xffffffff
[   19.860477] eqep 48302180.eqep: QPOSCNT:0x00000000
[   19.860485] eqep 48302180.eqep: omit_interrupt:0
[   19.860492] eqep 48302180.eqep: QEINT:0x0800
[   19.860500] eqep 48302180.eqep: QUPRD:0x05f5e100
[   19.860507] eqep 48302180.eqep: QEPCTL:0x009e write
[   19.860514] eqep 48302180.eqep: QEPCTL:0x009e read
[   19.860540] eqep 48302180.eqep: irq:191, clk_rate:100000000
[   19.920051] eqep 48304180.eqep: ver. 1.0
[   19.920277] eqep 48304180.eqep: count_mode:0
[   19.920288] eqep 48304180.eqep: invert_qa:1
[   19.920297] eqep 48304180.eqep: invert_qb:1
[   19.920306] eqep 48304180.eqep: invert_qi:0
[   19.920315] eqep 48304180.eqep: invert_qs:0
[   19.920324] eqep 48304180.eqep: swap_inputs:0
[   19.920333] eqep 48304180.eqep: QDECCTL:0x0180
[   19.920341] eqep 48304180.eqep: QPOSINIT:0x00000000
[   19.920348] eqep 48304180.eqep: QPOSMAX:0xffffffff
[   19.920356] eqep 48304180.eqep: QPOSCNT:0x00000000
[   19.920364] eqep 48304180.eqep: omit_interrupt:0
[   19.920371] eqep 48304180.eqep: QEINT:0x0800
[   19.920379] eqep 48304180.eqep: QUPRD:0x05f5e100
[   19.920386] eqep 48304180.eqep: QEPCTL:0x009e write
[   19.920394] eqep 48304180.eqep: QEPCTL:0x009e read
[   19.920420] eqep 48304180.eqep: irq:193, clk_rate:100000000
[   20.102532] CAN device driver interface
[   20.232192] c_can_platform 481cc000.can: c_can_platform device registered 
(regs=fa1cc000, irq=197)
[   20.232538] pruss_uio 4a300000.pruss: pins are not configured from the driver
[   20.294368] c_can_platform 481d0000.can: c_can_platform device registered 
(regs=fa1d0000, irq=198)
[   40.880784] random: crng init done

From 4242860c4fc7ce2f17b7a90411c1f2712ccef03c Mon Sep 17 00:00:00 2001
From: Robert Nelson <[email protected]>
Date: Wed, 2 Nov 2016 11:11:27 -0500
Subject: [PATCH] tieqep: forward port of Nathaniel Lewis eQEP driver

Signed-off-by: Robert Nelson <[email protected]>
---
 arch/arm/boot/dts/am33xx.dtsi |  31 ++
 drivers/misc/Kconfig          |  11 +
 drivers/misc/Makefile         |   1 +
 drivers/misc/tieqep.c         | 738 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 781 insertions(+)
 create mode 100644 drivers/misc/tieqep.c

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 771fc0a..20ccf52 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -701,6 +701,16 @@
 				status = "disabled";
 			};
 
+			eqep0: eqep@0x48300180 {
+				compatible = "ti,am33xx-eqep";
+				reg = <0x48300180 0x80>;
+				clocks = <&l4ls_gclk>;
+				clock-names = "fck";
+				interrupt-parent = <&intc>;
+				interrupts = <79>;
+				status = "disabled";
+			};
+
 			ehrpwm0: pwm@48300200 {
 				compatible = "ti,am3352-ehrpwm",
 					     "ti,am33xx-ehrpwm";
@@ -735,6 +745,17 @@
 				status = "disabled";
 			};
 
+
+			eqep1: eqep@0x48302180 {
+				compatible = "ti,am33xx-eqep";
+				reg = <0x48302180 0x80>;
+				clocks = <&l4ls_gclk>;
+				clock-names = "fck";
+				interrupt-parent = <&intc>;
+				interrupts = <88>;
+				status = "disabled";
+			};
+
 			ehrpwm1: pwm@48302200 {
 				compatible = "ti,am3352-ehrpwm",
 					     "ti,am33xx-ehrpwm";
@@ -769,6 +790,16 @@
 				status = "disabled";
 			};
 
+			eqep2: eqep@0x48304180 {
+				compatible = "ti,am33xx-eqep";
+				reg = <0x48304180 0x80>;
+				clocks = <&l4ls_gclk>;
+				clock-names = "fck";
+				interrupt-parent = <&intc>;
+				interrupts = <89>;
+				status = "disabled";
+			};
+
 			ehrpwm2: pwm@48304200 {
 				compatible = "ti,am3352-ehrpwm",
 					     "ti,am33xx-ehrpwm";
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index e3c6a92..b444d1db 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -785,6 +785,17 @@ config DEV_OVERLAYMGR
 	  Say Y here to include support for the automagical dev
 	  overlay manager.
 
+config TIEQEP
+	tristate "EQEP Hardware quadrature encoder controller"
+	depends on SOC_AM33XX
+	select PWM_TIPWMSS
+	help
+	  Driver support for the EQEP quadrature encoder controller AM33XX
+	  TI SOC
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called tieqep.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 9df0a01..a84dde4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_TIEQEP)		+= tieqep.o
 obj-$(CONFIG_BONE_CAPEMGR)	+= bone_capemgr.o
 obj-$(CONFIG_DEV_OVERLAYMGR)	+= devovmgr.o
 
diff --git a/drivers/misc/tieqep.c b/drivers/misc/tieqep.c
new file mode 100644
index 0000000..d2628847
--- /dev/null
+++ b/drivers/misc/tieqep.c
@@ -0,0 +1,738 @@
+/*
+ * TI eQEP driver for AM33xx devices
+ *
+ * Copyright (C) 2013 Nathaniel R. Lewis - http://teknoman117.wordpress.com/
+ * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * sysfs entries
+ *	 - position = absolute - current position; relative - last latched value
+ *	 - mode => 0 - absolute; 1 - relative
+ *	 - period => sampling period for the hardware
+ *	 - enable => 0 - eQEP disabled, 1 - eQEP enabled
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/input.h>
+
+/* eQEP register offsets from its base IO address */
+#define QPOSCNT		0x0000
+#define QPOSINIT	0x0004
+#define QPOSMAX		0x0008
+#define QPOSCMP		0x000C
+#define QPOSILAT	0x0010
+#define QPOSSLAT	0x0014
+#define QPOSLAT		0x0018
+#define QUTMR		0x001C
+#define QUPRD		0x0020
+#define QWDTMR		0x0024
+#define QWDPRD		0x0026
+#define QDECCTL		0x0028
+#define QEPCTL		0x002A
+#define QCAPCTL		0x002C
+#define QPOSCTL		0x002E
+#define QEINT		0x0030
+#define QFLG		0x0032
+#define QCLR		0x0034
+#define QFRC		0x0036
+#define QEPSTS		0x0038
+#define QCTMR		0x003A
+#define QCPRD		0x003C
+#define QCTMRLAT	0x003E
+#define QCPRDLAT	0x0040
+#define QREVID		0x005C
+
+#if 0	/* if you wanted another way to modify IP registers... */
+typedef volatile u32	REG32;
+typedef volatile u16	REG16;
+struct EQEP_REGS {
+	REG32	q_poscnt;		/*	0x00	position counter */
+	REG32	q_posinit;		/*	0x04	position counter initialization */
+	REG32	q_posmax;		/*	0x08	maximum position count */
+	REG32	q_poscmp;		/*	0x0C	position compare */
+	REG32	q_posilat;		/*	0x10	index position latch */
+	REG32	q_posslat;		/*	0x14	strobe position latch */
+	REG32	q_poslat;		/*	0x18	position counter latch */
+	REG32	q_utmr;			/*	0x1C	unit timer */
+	REG32	q_uprd;			/*	0x20	unit period */
+	REG16	q_wdtmr;		/*	0x24	watchdog timer */
+	REG16	q_wdprd;		/*	0x26	watchdog period */
+	REG16	q_decctl;		/*	0x28	decoder control */
+	REG16	q_epctl;		/*	0x2A	control register */
+	REG16	q_capctl;		/*	0x2C	capture control */
+	REG16	q_posctl;		/*	0x2E	position compare control */
+	REG16	q_eint;			/*	0x30	interrupt enable */
+	REG16	q_flg;			/*	0x32	interrupt flag */
+	REG16	q_clr;			/*	0x34	interrupt clear */
+	REG16	q_frc;			/*	0x36	interrupt force */
+	REG16	q_epsts;		/*	0x38	status */
+	REG16	q_ctmr;			/*	0x3A	capture timer */
+	REG16	q_cprd;			/*	0x3C	capture period */
+	REG16	q_ctmrlat;		/*	0x3E	capture timer latch */
+	REG16	q_prdlat;		/*	0x40	capture period latch */
+	char	q_fill1[0x5c-0x40];
+	REG32	q_revid;		/*	0x5C	revision id */
+};
+#endif
+
+
+/* Bits for the QDECTL register */
+#define QSRC1		(1 << 15)
+#define QSRC0		(1 << 14)
+#define SOEN		(1 << 13)
+#define SPSEL		(1 << 12)
+#define XCR		(1 << 11)
+#define SWAP		(1 << 10)
+#define IGATE		(1 << 9)
+#define QAP		(1 << 8)
+#define QBP		(1 << 7)
+#define QIP		(1 << 6)
+#define QSP		(1 << 5)
+
+/* Bits for the QEPCTL register */
+#define FREESOFT1	(1 << 15)
+#define FREESOFT0	(1 << 14)
+#define PCRM1		(1 << 13)
+#define PCRM0		(1 << 12)
+#define SEI1		(1 << 11)
+#define SEI0		(1 << 10)
+#define IEI1		(1 << 9)
+#define IEI0		(1 << 8)
+#define SWI		(1 << 7)
+#define SEL		(1 << 6)
+#define IEL1		(1 << 5)
+#define IEL0		(1 << 4)
+#define PHEN		(1 << 3)
+#define QCLM		(1 << 2)
+#define UTE		(1 << 1)
+#define WDE		(1 << 0)
+
+/* Bits for the QCAPCTL register */
+#define CEN		(1 << 15)
+#define CCPS2		(1 << 6)
+#define CCPS0		(1 << 5)
+#define CCPS1		(1 << 4)
+#define UPPS3		(1 << 3)
+#define UPPS2		(1 << 2)
+#define UPPS1		(1 << 1)
+#define UPPS0		(1 << 0)
+
+/* Bits for the QPOSCTL register */
+#define PCSHDW		(1 << 15)
+#define PCLOAD		(1 << 14)
+#define PCPOL		(1 << 13)
+#define PCE		(1 << 12)
+#define PCSPW11		(1 << 11)
+#define PCSPW10		(1 << 10)
+#define PCSPW9		(1 << 9)
+#define PCSPW8		(1 << 8)
+#define PCSPW7		(1 << 7)
+#define PCSPW6		(1 << 6)
+#define PCSPW5		(1 << 5)
+#define PCSPW4		(1 << 4)
+#define PCSPW3		(1 << 3)
+#define PCSPW2		(1 << 2)
+#define PCSPW1		(1 << 1)
+#define PCSPW0		(1 << 0)
+
+/* Bits for the interrupt registers */
+#define EQEP_INTERRUPT_MASK	0x0FFF
+#define UTOF			(1 << 11)
+
+/* Bits to control the clock in the PWMSS subsystem */
+#define PWMSS_EQEPCLK_EN	BIT(4)
+#define PWMSS_EQEPCLK_STOP_REQ	BIT(5)
+#define PWMSS_EQEPCLK_EN_ACK	BIT(4)
+
+/*
+ * Modes for the eQEP unit
+ *	Absolute - the position entry represents the current position of the encoder.
+ *		   Poll this value and it will be notified every period nanoseconds
+ *	Relative - the position entry represents the last latched position of the encoder
+ *		   This value is latched every period nanoseconds and the internal counter
+ *		   is subsequenty reset
+ */
+#define TIEQEP_MODE_ABSOLUTE	0
+#define TIEQEP_MODE_RELATIVE	1
+
+/* Structure defining the characteristics of the eQEP unit */
+struct eqep_chip
+{
+	/* Platform device for this eQEP unit */
+	struct platform_device *pdev;
+
+	/* Pointer to the base of the memory of the eQEP unit */
+	void __iomem	*mmio_base;
+
+	/* SYSCLKOUT to the eQEP unit */
+	u32		clk_rate;
+
+	/* IRQ for the eQEP unit */
+	u16		irq;
+
+	/* Mode of the eQEP unit */
+	u8		op_mode;
+
+	/* work stuct for the notify userspace work */
+	struct work_struct notify_work;
+
+	/* Backup for driver suspension */
+	u16		prior_qepctl;
+	u16		prior_qeint;
+};
+
+/* Notify userspace work */
+static void notify_handler(struct work_struct *work)
+{
+	/* Get a reference to the eQEP driver */
+	struct eqep_chip *eqep = container_of(work, struct eqep_chip, notify_work);
+
+	/* Notify the userspace */
+	sysfs_notify(&eqep->pdev->dev.kobj, NULL, "position");
+}
+
+/* eQEP Interrupt handler */
+static irqreturn_t eqep_irq_handler(int irq, void *dev_id)
+{
+	/* Get the instance information */
+	struct platform_device	*pdev = dev_id;
+	struct eqep_chip	*eqep = platform_get_drvdata(pdev);
+
+	/* Get the interrupt flags */
+	u16 iflags = readw(eqep->mmio_base + QFLG) & EQEP_INTERRUPT_MASK;
+
+	/* Check the interrupt source(s) */
+	if (iflags & UTOF) {
+		/* Handle the unit timer overflow interrupt by notifying any potential pollers */
+		schedule_work(&eqep->notify_work);
+	}
+
+	/* Clear interrupt flags (write back triggered flags to the clear register) */
+	writew(iflags, eqep->mmio_base + QCLR);
+
+	/* Return that the IRQ was handled successfully */
+	return IRQ_HANDLED;
+}
+
+/* Function to read whether the eQEP unit is enabled or disabled */
+static ssize_t eqep_get_enabled(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	/* Get the instance structure */
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	/* Read the qep control register and mask all but the enabled bit */
+	u16 enabled = readw(eqep->mmio_base + QEPCTL) & PHEN;
+
+	/* Return the target in string format */
+	return sprintf(buf, "%u\n", (enabled) ? 1 : 0);
+}
+
+/* Function to set if the eQEP is enabled */
+static ssize_t eqep_set_enabled(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	/* Get the instance structure */
+	int	rc;
+	u16	val;
+	u8	enabled;
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	/* Convert the input string to an 8 bit uint */
+	if ((rc = kstrtou8(buf, 0, &enabled)))
+		return rc;
+
+	/* Get the existing state of QEPCTL */
+	val = readw(eqep->mmio_base + QEPCTL);
+
+	/* If we passed a number that is not 0, enable the eQEP */
+	if (enabled)
+		/* Enable the eQEP (Set PHEN in QEPCTL) */
+		val |= PHEN;
+	else
+		/* Disable the eQEP (Clear PHEN in QEPCTL) */
+		val &= ~PHEN;
+
+	/* Write flags back to control register */
+	writew(val, eqep->mmio_base + QEPCTL);
+
+	/* Return buffer length consumed (all) */
+	return count;
+}
+
+/* Function to read the current position of the eQEP */
+static ssize_t eqep_get_position(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	s32 position = 0;
+
+	if (eqep->op_mode == TIEQEP_MODE_ABSOLUTE) {
+		position = readl(eqep->mmio_base + QPOSCNT);
+	} else if (eqep->op_mode == TIEQEP_MODE_RELATIVE) {
+		/* in relative mode, use the last latched value of the eQEP hardware */
+		position = readl(eqep->mmio_base + QPOSLAT);
+		dev_dbg(dev, "get_position:0x%08x\n", position);
+	}
+
+	return sprintf(buf, "%d\n", position);
+}
+
+/* Function to set the position of the eQEP hardware */
+static ssize_t eqep_set_position(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc;
+	s32 position;
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	if ((rc = kstrtos32(buf, 0, &position)))
+		return rc;
+
+	/*
+	 * If we are in absolute mode, set the position of the encoder,
+	 * discard relative mode because thats pointless
+	 */
+	if (eqep->op_mode == TIEQEP_MODE_ABSOLUTE) {
+		/* If absolute mode, set the current value of the eQEP hardware */
+		writel(position, eqep->mmio_base + QPOSCNT);
+	}
+
+	/* Return buffer length consumed (all) */
+	return count;
+}
+
+/* Function to read the period of the unit time event timer */
+static ssize_t eqep_get_timer_period(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+	u64 period;
+
+	/* Convert from counts per interrupt back into period_ns */
+	period = readl(eqep->mmio_base + QUPRD);
+	period = period * NSEC_PER_SEC;
+	do_div(period, eqep->clk_rate);
+
+	/* Otherwise write out the data */
+	return sprintf(buf, "%llu\n", period);
+}
+
+/* Function to set the unit timer period.  0 = off, greater than zero sets the period */
+static ssize_t eqep_set_timer_period(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int	rc;
+	u16	tmp;
+	u64	period;
+
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	if ((rc = kstrtou64(buf, 0, &period)))
+		return rc;
+
+	/* Disable the unit timer before modifying its period register */
+	tmp = readw(eqep->mmio_base + QEPCTL);
+	tmp &= ~(UTE | QCLM);
+	writew(tmp, eqep->mmio_base + QEPCTL);
+
+	/* Zero the unit timer counter register */
+	writel(0, eqep->mmio_base + QUTMR);
+
+	/* If the timer is enabled (a non-zero period has been passed) */
+	if (period) {
+		/* update the period */
+		period = period * eqep->clk_rate;
+		do_div(period, NSEC_PER_SEC);
+
+		dev_dbg(dev, "eqep_set_timer_period:%llu\n", period);
+
+		writel(period, eqep->mmio_base + QUPRD);
+
+		/* Enable unit timer, and latch QPOSLAT to QPOSCNT on timer expiration */
+		tmp |= UTE | QCLM;
+		writew(tmp, eqep->mmio_base + QEPCTL);
+	}
+
+	return count;
+}
+
+/* Function to read the mode of the eQEP hardware */
+static ssize_t eqep_get_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", eqep->op_mode);
+}
+
+/* Function to set the mode of the eQEP hardware */
+static ssize_t eqep_set_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int 	rc;
+	u16 	val;
+	u8	tmp_mode;
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	if ((rc = kstrtou8(buf, 0, &tmp_mode)))
+		return rc;
+
+	dev_dbg(dev, "eqep_set_mode:%d\n", tmp_mode);
+
+	val = readw(eqep->mmio_base + QEPCTL);
+
+	if (tmp_mode == TIEQEP_MODE_ABSOLUTE) {
+		/*
+		 * In absolute mode, don't reset the hardware based on time,
+		 * so disable the unit timer position reset (Set PCRM[1:0] = 0)
+		 */
+		val &= ~(PCRM1 | PCRM0);
+
+		eqep->op_mode = TIEQEP_MODE_ABSOLUTE;
+	} else if (tmp_mode == TIEQEP_MODE_RELATIVE) {
+		/*
+		 * In relative mode, latch the value of the eQEP hardware on the
+		 * overflow of the unit timer.	So enable the unit timer position reset
+		 * (Set PCRM[1:0] = 3)
+		 */
+		val |= PCRM1 | PCRM0;
+
+		eqep->op_mode = TIEQEP_MODE_RELATIVE;
+	}
+
+	writew(val, eqep->mmio_base + QEPCTL);
+
+	return count;
+}
+
+/* Bind read/write functions to sysfs entries */
+static DEVICE_ATTR(enabled,	0644, eqep_get_enabled,		eqep_set_enabled);
+static DEVICE_ATTR(position,	0644, eqep_get_position,	eqep_set_position);
+static DEVICE_ATTR(period,	0644, eqep_get_timer_period,	eqep_set_timer_period);
+static DEVICE_ATTR(mode,	0644, eqep_get_mode,		eqep_set_mode);
+
+/* Array holding all of the sysfs entries */
+static const struct attribute *eqep_attrs[] = {
+	&dev_attr_enabled.attr,
+	&dev_attr_position.attr,
+	&dev_attr_period.attr,
+	&dev_attr_mode.attr,
+	NULL,
+};
+
+/* Driver function group */
+static const struct attribute_group eqep_device_attr_group = {
+	.attrs = (struct attribute **) eqep_attrs,
+};
+
+/* Driver compatibility list */
+static struct of_device_id eqep_of_match[] =
+{
+	{ .compatible = "ti,am33xx-eqep" },
+	{ }
+};
+
+/* Register our compatibilities for device trees */
+MODULE_DEVICE_TABLE(of, eqep_of_match);
+
+/* Create an instance of the eQEP driver */
+static int eqep_probe(struct platform_device *pdev)
+{
+	struct resource	 *r;
+	struct clk	 *clk;
+	struct eqep_chip *eqep;
+	struct pinctrl	 *pinctrl;
+
+	u64	period;
+	u16	status;
+	u32	value;
+
+	dev_info(&pdev->dev, "ver. 1.0\n");
+
+	/* Select pins provided through the device tree */
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl))
+	{
+		dev_warn(&pdev->dev, "unable to select pin group\n");
+	}
+
+	/* Allocate a eqep_driver object */
+	eqep = devm_kzalloc(&pdev->dev, sizeof(struct eqep_chip), GFP_KERNEL);
+	if (!eqep) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* Get a handle to the system clock object */
+	clk = devm_clk_get(pdev->dev.parent, "fck");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(clk);
+	}
+
+	/* Get the frequency of the system clock */
+	eqep->clk_rate = clk_get_rate(clk);
+	if (!eqep->clk_rate) {
+		dev_err(&pdev->dev, "failed to get clock rate\n");
+		return -EINVAL;
+	}
+
+	/* Get a resource containing the IRQ for this eQEP controller */
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (unlikely(!r)) {
+		dev_err(&pdev->dev, "Invalid IRQ resource\n");
+		return -ENODEV;
+	}
+
+	/* Store the irq */
+	eqep->irq = r->start;
+
+	/* Get a resource containing the requested (from DT) memory address and range of eQEP controller */
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	/* Remap the eQEP controller memory into our own memory space */
+	eqep->mmio_base = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(eqep->mmio_base))
+		return PTR_ERR(eqep->mmio_base);
+
+	/* Store the platform device in our eQEP data structure for later usage */
+	eqep->pdev = pdev;
+
+	/* Subscribe to the eQEP interrupt */
+	if (request_irq(eqep->irq, eqep_irq_handler, IRQF_IRQPOLL, "eqep_interrupt", pdev))
+	{
+		dev_err(&pdev->dev, "unable to request irq for eQEP\n");
+		return -ENODEV;
+	}
+
+	/* Register controls to sysfs */
+	if (sysfs_create_group(&pdev->dev.kobj, &eqep_device_attr_group))
+	{
+		dev_err(&pdev->dev, "sysfs creation failed\n");
+		return -EINVAL;
+	}
+
+	/* set QDECCTL */
+	status = 0;	/* default to Quadrature count mode, QSRC1 & QSRC0 = 0 */
+
+	/* set QSRC1 & QSRC0 bits, one of 4 count_modes. */
+	if (!of_property_read_u32(pdev->dev.of_node, "count_mode", &value) && value <= 3) {
+		status |= value << 14;
+
+		/*
+		 * in count up or count down mode, count on rising edge only
+		 * not on both edges.
+		 */
+		if (value >= 2)
+			status |= XCR;
+	}
+	dev_info(&pdev->dev, "count_mode:%d\n", value);
+
+	/* Should we invert the qa input */
+	if (!of_property_read_u32(pdev->dev.of_node, "invert_qa", &value))
+		status = value ? status | QAP : status & ~QAP;
+	dev_info(&pdev->dev, "invert_qa:%d\n", value);
+
+	/* Should we invert the qb input */
+	if (!of_property_read_u32(pdev->dev.of_node, "invert_qb", &value))
+		status = value ? status | QBP : status & ~QBP;
+	dev_info(&pdev->dev, "invert_qb:%d\n", value);
+
+	/* Should we invert the index input */
+	if (!of_property_read_u32(pdev->dev.of_node, "invert_qi", &value))
+		status = value ? status | QIP : status & ~QIP;
+	dev_info(&pdev->dev, "invert_qi:%d\n", value);
+
+	/* Should we invert the strobe input */
+	if (!of_property_read_u32(pdev->dev.of_node, "invert_qs", &value))
+		status = value ? status | QSP : status & ~QSP;
+	dev_info(&pdev->dev, "invert_qs:%d\n", value);
+
+	/* Should we swap the cha and chb inputs */
+	if (!of_property_read_u32(pdev->dev.of_node, "swap_inputs", &value))
+		status = value ? status | SWAP : status & ~SWAP;
+	dev_info(&pdev->dev, "swap_inputs:%d\n", value);
+
+	dev_info(&pdev->dev, "QDECCTL:0x%04x\n", status);
+
+	/* Write the decoder control settings back to the control register */
+	writew(status, eqep->mmio_base + QDECCTL);
+
+	writel( 0, eqep->mmio_base + QPOSINIT);
+	writel(~0, eqep->mmio_base + QPOSMAX);
+	writel( 0, eqep->mmio_base + QPOSCNT);
+
+	dev_info(&pdev->dev, "QPOSINIT:0x%08x\n", readl(eqep->mmio_base + QPOSINIT));
+	dev_info(&pdev->dev, "QPOSMAX:0x%08x\n", readl(eqep->mmio_base + QPOSMAX));
+	dev_info(&pdev->dev, "QPOSCNT:0x%08x\n", readl(eqep->mmio_base + QPOSCNT));
+
+	status = UTOF;		/* Enable Unit Time Period interrupt. */
+	if (!of_property_read_u32(pdev->dev.of_node, "omit_interrupt", &value) && value) {
+		status = 0;	/* no interrupt */
+	}
+	writew(status, eqep->mmio_base + QEINT);
+	dev_info(&pdev->dev, "omit_interrupt:%d\n", value);
+	dev_info(&pdev->dev, "QEINT:0x%04x\n", status);
+
+	/* Calculate the timer ticks per second */
+	period = 1000000000;
+	period = period * eqep->clk_rate;
+	do_div(period, NSEC_PER_SEC);
+
+	/* Set this period into the unit timer period register */
+	writel(period, eqep->mmio_base + QUPRD);
+	dev_info(&pdev->dev, "QUPRD:0x%08x\n", (u32) period);
+
+	/*
+	 * Enable the eQEP with basic position counting turned on
+	 * PHEN - Quadrature position counter enable bit
+	 * UTE	- unit timer enable
+	 * QCLM - latch QPOSLAT to QPOSCNT upon unit timer overflow
+	 * IEL0 - Latch QPOSILAT on index signal.  Rising or falling, IEL[1:0] = 0 is reserved
+	 * SWI	- Software initialization of position count register, i.e. set QPOSCNT <= QPOSINIT,
+	 *  but this bit was not being reset by hardware as advertised in TRM,
+	 *  (so omit & clear QPOSCNT manually elsewhere?)
+	 */
+	status = PHEN | UTE | QCLM | IEL0 | SWI;
+	writew(status, eqep->mmio_base + QEPCTL);
+	dev_info(&pdev->dev, "QEPCTL:0x%04x write\n", status);
+	dev_info(&pdev->dev, "QEPCTL:0x%04x read\n", readw(eqep->mmio_base + QEPCTL));
+
+	/* We default to absolute mode */
+	eqep->op_mode = TIEQEP_MODE_ABSOLUTE;
+
+	/* Enable the power management runtime */
+	pm_runtime_enable(&pdev->dev);
+
+	/* Increment the device usage count and run pm_runtime_resume() */
+	pm_runtime_get_sync(&pdev->dev);
+
+	/* Initialize the notify work struture */
+	INIT_WORK(&eqep->notify_work, notify_handler);
+
+	/* Decrement the device usage count (twice) and run pm_runtime_idle() if zero */
+	pm_runtime_put_sync(&pdev->dev);
+
+	/* Set the platform driver data to the data object we've been creating for the eQEP unit */
+	platform_set_drvdata(pdev, eqep);
+
+	/* Success! */
+	dev_info(&pdev->dev, "irq:%d, clk_rate:%u\n", eqep->irq, eqep->clk_rate);
+	return 0;
+}
+
+/* Remove an instance of the eQEP driver */
+static int eqep_remove(struct platform_device *pdev)
+{
+	/* Get the eQEP driver data from the platform device structure */
+	struct eqep_chip *eqep = platform_get_drvdata(pdev);
+
+	/* Cancel work */
+	cancel_work_sync(&eqep->notify_work);
+
+	/* Unmap from sysfs */
+	sysfs_remove_group(&pdev->dev.kobj, &eqep_device_attr_group);
+
+	/* Release important assets */
+	free_irq(eqep->irq, pdev);
+
+	/* Increment the device usage count and run pm_runtime_resume() */
+	pm_runtime_get_sync(&pdev->dev);
+
+	/* Decrement the device usage count (twice) and run pm_runtime_idle() if zero */
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_put_sync(&pdev->dev);
+
+	/* Disable the runtime power management of this device */
+	pm_runtime_disable(&pdev->dev);
+
+	/* Return success */
+	return 0;
+}
+
+/* Power management suspend device */
+static int eqep_suspend(struct device *dev)
+{
+	/* Get the eqep driver information */
+	struct eqep_chip   *eqep = dev_get_drvdata(dev);
+	u16					tmp;
+
+	/* Shut down interrupts */
+	eqep->prior_qeint = readw(eqep->mmio_base + QEINT);
+	tmp = eqep->prior_qeint & ~UTOF;
+	writew(tmp, eqep->mmio_base + QEINT);
+
+	/* Get the existing state of QEPCTL */
+	eqep->prior_qepctl = readw(eqep->mmio_base + QEPCTL);
+
+	/* Disable eQEP controller */
+	writew(eqep->prior_qepctl & ~PHEN, eqep->mmio_base + QEPCTL);
+
+	/* Decrement the device usage count and run pm_runtime_idle() if zero */
+	pm_runtime_put_sync(dev);
+
+	/* Return success */
+	return 0;
+}
+
+/* Power management wake device back up */
+static int eqep_resume(struct device *dev)
+{
+	/* Get the eqep driver information */
+	struct eqep_chip *eqep = dev_get_drvdata(dev);
+
+	/* Restore interrupt enabled register */
+	writew(eqep->prior_qeint, eqep->mmio_base + QEINT);
+
+	/* Restore prior qep control register */
+	writew(eqep->prior_qepctl, eqep->mmio_base + QEPCTL);
+
+	/* Increment the device usage count and run pm_runtime_resume() */
+	pm_runtime_get_sync(dev);
+
+	/* Success */
+	return 0;
+}
+
+/* create pm functions object */
+static SIMPLE_DEV_PM_OPS(eqep_pm_ops, eqep_suspend, eqep_resume);
+
+/* Platform driver information */
+static struct platform_driver eqep_driver = {
+	.driver = {
+		.name	= "eqep",
+		.owner	= THIS_MODULE,
+		.pm	= &eqep_pm_ops,
+		.of_match_table = eqep_of_match,
+	},
+	.probe = eqep_probe,
+	.remove = eqep_remove,
+};
+
+/* Register this platform driver */
+module_platform_driver(eqep_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("TI eQEP driver");
+MODULE_AUTHOR("Nathaniel R. Lewis");
+MODULE_LICENSE("GPL");
-- 
2.10.1

root@beaglebone:~# cat 
/sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position 

[  615.400669] Unhandled fault: external abort on non-linefetch (0x1028) at 
0xfa304180
[  615.408497] pgd = dc470000
[  615.411260] [fa304180] *pgd=48211452(bad)
[  615.415402] Internal error: : 1028 [#1] PREEMPT SMP ARM
[  615.420707] Modules linked in: spidev uio_pruss c_can_platform c_can can_dev 
spi_omap2_mcspi pwm_tiecap tieqep pwm_tiehrpwm omap_aes_driver crypto_engine 
omap_sham 8021q garp mrp stp llc usb_f_acm u_serial usb_f_rndis u_ether 
libcomposite cpufreq_powersave cpufreq_userspace omap_rng cpufreq_conservative 
rng_core cpufreq_ondemand evdev tps65217_charger uio_pdrv_genirq uio
[  615.454633] CPU: 0 PID: 734 Comm: cat Not tainted 4.9.6-ti-r17 #1
[  615.460813] Hardware name: Generic AM33XX (Flattened Device Tree)
[  615.466995] task: dc17a700 task.stack: dc192000
[  615.471643] PC is at eqep_get_position+0x68/0x90 [tieqep]
[  615.477147] LR is at dev_attr_show+0x2c/0x58
[  615.481493] pc : [<bf0e3310>]    lr : [<c0893f1c>]    psr: 600f0013
sp : dc193e30  ip : 00000000  fp : dc193e44
[  615.493113] r10: 00000001  r9 : dc776f80  r8 : da8d5c18
[  615.498470] r7 : dc77f000  r6 : c0d855b4  r5 : dc77f000  r4 : dc430900
[  615.505122] r3 : fa304180  r2 : dc77f000  r1 : bf0e452c  r0 : da8d5c10
[  615.511777] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
[  615.519045] Control: 10c5387d  Table: 9c470019  DAC: 00000051
[  615.524907] Process cat (pid: 734, stack limit = 0xdc192218)
[  615.530682] Stack: (0xdc193e30 to 0xdc194000)
[  615.535151] 3e20:                                     dc430900 bf0e452c 
dc193e5c dc193e48
[  615.543520] 3e40: c0893f1c bf0e32b4 dc430900 00001000 dc193e84 dc193e60 
c035ffa4 c0893efc
[  615.551886] 3e60: dc430900 00000000 da814540 c130414c dc193f70 00000001 
dc193e94 dc193e88
[  615.560252] 3e80: c035e954 c035ff18 dc193ef4 dc193e98 c03013dc c035e92c 
da6f22e0 dc193f70
[  615.568618] 3ea0: dc430930 b6ea1000 dc193eb0 00020000 00000000 00000000 
00020000 da814548
[  615.576984] 3ec0: 00000004 00040906 00000000 00020000 da814540 b6ea1000 
dc193f70 b6ea1000
[  615.585350] 3ee0: dc776f80 00000000 dc193f2c dc193ef8 c035f724 c0301200 
dc193f3c dc193f08
[  615.593715] 3f00: c02d8fac 00020000 da814540 b6ea1000 dc193f70 b6ea1000 
dc192000 00000000
[  615.602081] 3f20: dc193f3c dc193f30 c02d8290 c035f5fc dc193f6c dc193f40 
c02d90ec c02d8274
[  615.610448] 3f40: 000b6ea0 c02fae4c dc193f6c da814540 c130414c da814540 
00020000 b6ea1000
[  615.618815] 3f60: dc193fa4 dc193f70 c02da388 c02d905c 00000000 00000000 
00000022 00040906
[  615.627180] 3f80: 00000000 00020000 00020000 b6ea1000 00000003 c01092c4 
00000000 dc193fa8
[  615.635546] 3fa0: c0109100 c02da330 00020000 00020000 00000003 b6ea1000 
00020000 000271c4
[  615.643912] 3fc0: 00020000 00020000 b6ea1000 00000003 7fffe000 00000000 
00000000 00020000
[  615.652279] 3fe0: 00000000 bea5caa4 00013835 b6f4ac76 400f0030 00000003 
7b7b33da 9e96bd2f
[  615.660717] [<bf0e3310>] (eqep_get_position [tieqep]) from [<c0893f1c>] 
(dev_attr_show+0x2c/0x58)
[  615.669821] [<c0893f1c>] (dev_attr_show) from [<c035ffa4>] 
(sysfs_kf_seq_show+0x98/0x108)
[  615.678205] [<c035ffa4>] (sysfs_kf_seq_show) from [<c035e954>] 
(kernfs_seq_show+0x34/0x38)
[  615.686666] [<c035e954>] (kernfs_seq_show) from [<c03013dc>] 
(seq_read+0x1e8/0x538)
[  615.694512] [<c03013dc>] (seq_read) from [<c035f724>] 
(kernfs_fop_read+0x134/0x1c0)
[  615.702358] [<c035f724>] (kernfs_fop_read) from [<c02d8290>] 
(__vfs_read+0x28/0x48)
[  615.710202] [<c02d8290>] (__vfs_read) from [<c02d90ec>] (vfs_read+0x9c/0x168)
[  615.717483] [<c02d90ec>] (vfs_read) from [<c02da388>] (SyS_read+0x64/0xcc)
[  615.724516] [<c02da388>] (SyS_read) from [<c0109100>] 
(ret_fast_syscall+0x0/0x3c)
[  615.732188] Code: e1a04003 e320f000 eafffff3 e5933004 (e5934000) 
[  615.738437] ---[ end trace 7936ced3381948b2 ]---

/*
 * TI eQEP driver for AM33xx devices
 *
 * Copyright (C) 2013 Nathaniel R. Lewis - http://teknoman117.wordpress.com/
 * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * sysfs entries
 *	 - position = absolute - current position; relative - last latched value
 *	 - mode => 0 - absolute; 1 - relative
 *	 - period => sampling period for the hardware
 *	 - enable => 0 - eQEP disabled, 1 - eQEP enabled
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/input.h>

/* eQEP register offsets from its base IO address */
#define QPOSCNT		0x0000
#define QPOSINIT	0x0004
#define QPOSMAX		0x0008
#define QPOSCMP		0x000C
#define QPOSILAT	0x0010
#define QPOSSLAT	0x0014
#define QPOSLAT		0x0018
#define QUTMR		0x001C
#define QUPRD		0x0020
#define QWDTMR		0x0024
#define QWDPRD		0x0026
#define QDECCTL		0x0028
#define QEPCTL		0x002A
#define QCAPCTL		0x002C
#define QPOSCTL		0x002E
#define QEINT		0x0030
#define QFLG		0x0032
#define QCLR		0x0034
#define QFRC		0x0036
#define QEPSTS		0x0038
#define QCTMR		0x003A
#define QCPRD		0x003C
#define QCTMRLAT	0x003E
#define QCPRDLAT	0x0040
#define QREVID		0x005C

#if 0	/* if you wanted another way to modify IP registers... */
typedef volatile u32	REG32;
typedef volatile u16	REG16;
struct EQEP_REGS {
	REG32	q_poscnt;		/*	0x00	position counter */
	REG32	q_posinit;		/*	0x04	position counter initialization */
	REG32	q_posmax;		/*	0x08	maximum position count */
	REG32	q_poscmp;		/*	0x0C	position compare */
	REG32	q_posilat;		/*	0x10	index position latch */
	REG32	q_posslat;		/*	0x14	strobe position latch */
	REG32	q_poslat;		/*	0x18	position counter latch */
	REG32	q_utmr;			/*	0x1C	unit timer */
	REG32	q_uprd;			/*	0x20	unit period */
	REG16	q_wdtmr;		/*	0x24	watchdog timer */
	REG16	q_wdprd;		/*	0x26	watchdog period */
	REG16	q_decctl;		/*	0x28	decoder control */
	REG16	q_epctl;		/*	0x2A	control register */
	REG16	q_capctl;		/*	0x2C	capture control */
	REG16	q_posctl;		/*	0x2E	position compare control */
	REG16	q_eint;			/*	0x30	interrupt enable */
	REG16	q_flg;			/*	0x32	interrupt flag */
	REG16	q_clr;			/*	0x34	interrupt clear */
	REG16	q_frc;			/*	0x36	interrupt force */
	REG16	q_epsts;		/*	0x38	status */
	REG16	q_ctmr;			/*	0x3A	capture timer */
	REG16	q_cprd;			/*	0x3C	capture period */
	REG16	q_ctmrlat;		/*	0x3E	capture timer latch */
	REG16	q_prdlat;		/*	0x40	capture period latch */
	char	q_fill1[0x5c-0x40];
	REG32	q_revid;		/*	0x5C	revision id */
};
#endif


/* Bits for the QDECTL register */
#define QSRC1		(1 << 15)
#define QSRC0		(1 << 14)
#define SOEN		(1 << 13)
#define SPSEL		(1 << 12)
#define XCR		(1 << 11)
#define SWAP		(1 << 10)
#define IGATE		(1 << 9)
#define QAP		(1 << 8)
#define QBP		(1 << 7)
#define QIP		(1 << 6)
#define QSP		(1 << 5)

/* Bits for the QEPCTL register */
#define FREESOFT1	(1 << 15)
#define FREESOFT0	(1 << 14)
#define PCRM1		(1 << 13)
#define PCRM0		(1 << 12)
#define SEI1		(1 << 11)
#define SEI0		(1 << 10)
#define IEI1		(1 << 9)
#define IEI0		(1 << 8)
#define SWI		(1 << 7)
#define SEL		(1 << 6)
#define IEL1		(1 << 5)
#define IEL0		(1 << 4)
#define PHEN		(1 << 3)
#define QCLM		(1 << 2)
#define UTE		(1 << 1)
#define WDE		(1 << 0)

/* Bits for the QCAPCTL register */
#define CEN		(1 << 15)
#define CCPS2		(1 << 6)
#define CCPS0		(1 << 5)
#define CCPS1		(1 << 4)
#define UPPS3		(1 << 3)
#define UPPS2		(1 << 2)
#define UPPS1		(1 << 1)
#define UPPS0		(1 << 0)

/* Bits for the QPOSCTL register */
#define PCSHDW		(1 << 15)
#define PCLOAD		(1 << 14)
#define PCPOL		(1 << 13)
#define PCE		(1 << 12)
#define PCSPW11		(1 << 11)
#define PCSPW10		(1 << 10)
#define PCSPW9		(1 << 9)
#define PCSPW8		(1 << 8)
#define PCSPW7		(1 << 7)
#define PCSPW6		(1 << 6)
#define PCSPW5		(1 << 5)
#define PCSPW4		(1 << 4)
#define PCSPW3		(1 << 3)
#define PCSPW2		(1 << 2)
#define PCSPW1		(1 << 1)
#define PCSPW0		(1 << 0)

/* Bits for the interrupt registers */
#define EQEP_INTERRUPT_MASK	0x0FFF
#define UTOF			(1 << 11)

/* Bits to control the clock in the PWMSS subsystem */
#define PWMSS_EQEPCLK_EN	BIT(4)
#define PWMSS_EQEPCLK_STOP_REQ	BIT(5)
#define PWMSS_EQEPCLK_EN_ACK	BIT(4)

/*
 * Modes for the eQEP unit
 *	Absolute - the position entry represents the current position of the encoder.
 *		   Poll this value and it will be notified every period nanoseconds
 *	Relative - the position entry represents the last latched position of the encoder
 *		   This value is latched every period nanoseconds and the internal counter
 *		   is subsequenty reset
 */
#define TIEQEP_MODE_ABSOLUTE	0
#define TIEQEP_MODE_RELATIVE	1

/* Structure defining the characteristics of the eQEP unit */
struct eqep_chip
{
	/* Platform device for this eQEP unit */
	struct platform_device *pdev;

	/* Pointer to the base of the memory of the eQEP unit */
	void __iomem	*mmio_base;

	/* SYSCLKOUT to the eQEP unit */
	u32		clk_rate;

	/* IRQ for the eQEP unit */
	u16		irq;

	/* Mode of the eQEP unit */
	u8		op_mode;

	/* work stuct for the notify userspace work */
	struct work_struct notify_work;

	/* Backup for driver suspension */
	u16		prior_qepctl;
	u16		prior_qeint;
};

/* Notify userspace work */
static void notify_handler(struct work_struct *work)
{
	/* Get a reference to the eQEP driver */
	struct eqep_chip *eqep = container_of(work, struct eqep_chip, notify_work);

	/* Notify the userspace */
	sysfs_notify(&eqep->pdev->dev.kobj, NULL, "position");
}

/* eQEP Interrupt handler */
static irqreturn_t eqep_irq_handler(int irq, void *dev_id)
{
	/* Get the instance information */
	struct platform_device	*pdev = dev_id;
	struct eqep_chip	*eqep = platform_get_drvdata(pdev);

	/* Get the interrupt flags */
	u16 iflags = readw(eqep->mmio_base + QFLG) & EQEP_INTERRUPT_MASK;

	/* Check the interrupt source(s) */
	if (iflags & UTOF) {
		/* Handle the unit timer overflow interrupt by notifying any potential pollers */
		schedule_work(&eqep->notify_work);
	}

	/* Clear interrupt flags (write back triggered flags to the clear register) */
	writew(iflags, eqep->mmio_base + QCLR);

	/* Return that the IRQ was handled successfully */
	return IRQ_HANDLED;
}

/* Function to read whether the eQEP unit is enabled or disabled */
static ssize_t eqep_get_enabled(struct device *dev, struct device_attribute *attr, char *buf)
{
	/* Get the instance structure */
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	/* Read the qep control register and mask all but the enabled bit */
	u16 enabled = readw(eqep->mmio_base + QEPCTL) & PHEN;

	/* Return the target in string format */
	return sprintf(buf, "%u\n", (enabled) ? 1 : 0);
}

/* Function to set if the eQEP is enabled */
static ssize_t eqep_set_enabled(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	/* Get the instance structure */
	int	rc;
	u16	val;
	u8	enabled;
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	/* Convert the input string to an 8 bit uint */
	if ((rc = kstrtou8(buf, 0, &enabled)))
		return rc;

	/* Get the existing state of QEPCTL */
	val = readw(eqep->mmio_base + QEPCTL);

	/* If we passed a number that is not 0, enable the eQEP */
	if (enabled)
		/* Enable the eQEP (Set PHEN in QEPCTL) */
		val |= PHEN;
	else
		/* Disable the eQEP (Clear PHEN in QEPCTL) */
		val &= ~PHEN;

	/* Write flags back to control register */
	writew(val, eqep->mmio_base + QEPCTL);

	/* Return buffer length consumed (all) */
	return count;
}

/* Function to read the current position of the eQEP */
static ssize_t eqep_get_position(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	s32 position = 0;

	if (eqep->op_mode == TIEQEP_MODE_ABSOLUTE) {
		position = readl(eqep->mmio_base + QPOSCNT);
	} else if (eqep->op_mode == TIEQEP_MODE_RELATIVE) {
		/* in relative mode, use the last latched value of the eQEP hardware */
		position = readl(eqep->mmio_base + QPOSLAT);
		dev_dbg(dev, "get_position:0x%08x\n", position);
	}

	return sprintf(buf, "%d\n", position);
}

/* Function to set the position of the eQEP hardware */
static ssize_t eqep_set_position(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int rc;
	s32 position;
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	if ((rc = kstrtos32(buf, 0, &position)))
		return rc;

	/*
	 * If we are in absolute mode, set the position of the encoder,
	 * discard relative mode because thats pointless
	 */
	if (eqep->op_mode == TIEQEP_MODE_ABSOLUTE) {
		/* If absolute mode, set the current value of the eQEP hardware */
		writel(position, eqep->mmio_base + QPOSCNT);
	}

	/* Return buffer length consumed (all) */
	return count;
}

/* Function to read the period of the unit time event timer */
static ssize_t eqep_get_timer_period(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct eqep_chip *eqep = dev_get_drvdata(dev);
	u64 period;

	/* Convert from counts per interrupt back into period_ns */
	period = readl(eqep->mmio_base + QUPRD);
	period = period * NSEC_PER_SEC;
	do_div(period, eqep->clk_rate);

	/* Otherwise write out the data */
	return sprintf(buf, "%llu\n", period);
}

/* Function to set the unit timer period.  0 = off, greater than zero sets the period */
static ssize_t eqep_set_timer_period(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int	rc;
	u16	tmp;
	u64	period;

	struct eqep_chip *eqep = dev_get_drvdata(dev);

	if ((rc = kstrtou64(buf, 0, &period)))
		return rc;

	/* Disable the unit timer before modifying its period register */
	tmp = readw(eqep->mmio_base + QEPCTL);
	tmp &= ~(UTE | QCLM);
	writew(tmp, eqep->mmio_base + QEPCTL);

	/* Zero the unit timer counter register */
	writel(0, eqep->mmio_base + QUTMR);

	/* If the timer is enabled (a non-zero period has been passed) */
	if (period) {
		/* update the period */
		period = period * eqep->clk_rate;
		do_div(period, NSEC_PER_SEC);

		dev_dbg(dev, "eqep_set_timer_period:%llu\n", period);

		writel(period, eqep->mmio_base + QUPRD);

		/* Enable unit timer, and latch QPOSLAT to QPOSCNT on timer expiration */
		tmp |= UTE | QCLM;
		writew(tmp, eqep->mmio_base + QEPCTL);
	}

	return count;
}

/* Function to read the mode of the eQEP hardware */
static ssize_t eqep_get_mode(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	return sprintf(buf, "%u\n", eqep->op_mode);
}

/* Function to set the mode of the eQEP hardware */
static ssize_t eqep_set_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int 	rc;
	u16 	val;
	u8	tmp_mode;
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	if ((rc = kstrtou8(buf, 0, &tmp_mode)))
		return rc;

	dev_dbg(dev, "eqep_set_mode:%d\n", tmp_mode);

	val = readw(eqep->mmio_base + QEPCTL);

	if (tmp_mode == TIEQEP_MODE_ABSOLUTE) {
		/*
		 * In absolute mode, don't reset the hardware based on time,
		 * so disable the unit timer position reset (Set PCRM[1:0] = 0)
		 */
		val &= ~(PCRM1 | PCRM0);

		eqep->op_mode = TIEQEP_MODE_ABSOLUTE;
	} else if (tmp_mode == TIEQEP_MODE_RELATIVE) {
		/*
		 * In relative mode, latch the value of the eQEP hardware on the
		 * overflow of the unit timer.	So enable the unit timer position reset
		 * (Set PCRM[1:0] = 3)
		 */
		val |= PCRM1 | PCRM0;

		eqep->op_mode = TIEQEP_MODE_RELATIVE;
	}

	writew(val, eqep->mmio_base + QEPCTL);

	return count;
}

/* Bind read/write functions to sysfs entries */
static DEVICE_ATTR(enabled,	0644, eqep_get_enabled,		eqep_set_enabled);
static DEVICE_ATTR(position,	0644, eqep_get_position,	eqep_set_position);
static DEVICE_ATTR(period,	0644, eqep_get_timer_period,	eqep_set_timer_period);
static DEVICE_ATTR(mode,	0644, eqep_get_mode,		eqep_set_mode);

/* Array holding all of the sysfs entries */
static const struct attribute *eqep_attrs[] = {
	&dev_attr_enabled.attr,
	&dev_attr_position.attr,
	&dev_attr_period.attr,
	&dev_attr_mode.attr,
	NULL,
};

/* Driver function group */
static const struct attribute_group eqep_device_attr_group = {
	.attrs = (struct attribute **) eqep_attrs,
};

/* Driver compatibility list */
static struct of_device_id eqep_of_match[] =
{
	{ .compatible = "ti,am33xx-eqep" },
	{ }
};

/* Register our compatibilities for device trees */
MODULE_DEVICE_TABLE(of, eqep_of_match);

/* Create an instance of the eQEP driver */
static int eqep_probe(struct platform_device *pdev)
{
	struct resource	 *r;
	struct clk	 *clk;
	struct eqep_chip *eqep;
	struct pinctrl	 *pinctrl;

	u64	period;
	u16	status;
	u32	value;

	dev_info(&pdev->dev, "ver. 1.0\n");

	/* Select pins provided through the device tree */
	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
	if (IS_ERR(pinctrl))
	{
		dev_warn(&pdev->dev, "unable to select pin group\n");
	}

	/* Allocate a eqep_driver object */
	eqep = devm_kzalloc(&pdev->dev, sizeof(struct eqep_chip), GFP_KERNEL);
	if (!eqep) {
		dev_err(&pdev->dev, "failed to allocate memory\n");
		return -ENOMEM;
	}

	/* Get a handle to the system clock object */
	clk = devm_clk_get(pdev->dev.parent, "fck");
	if (IS_ERR(clk)) {
		dev_err(&pdev->dev, "failed to get clock\n");
		return PTR_ERR(clk);
	}

	/* Get the frequency of the system clock */
	eqep->clk_rate = clk_get_rate(clk);
	if (!eqep->clk_rate) {
		dev_err(&pdev->dev, "failed to get clock rate\n");
		return -EINVAL;
	}

	/* Get a resource containing the IRQ for this eQEP controller */
	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (unlikely(!r)) {
		dev_err(&pdev->dev, "Invalid IRQ resource\n");
		return -ENODEV;
	}

	/* Store the irq */
	eqep->irq = r->start;

	/* Get a resource containing the requested (from DT) memory address and range of eQEP controller */
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		dev_err(&pdev->dev, "no memory resource defined\n");
		return -ENODEV;
	}

	/* Remap the eQEP controller memory into our own memory space */
	eqep->mmio_base = devm_ioremap_resource(&pdev->dev, r);
	if (IS_ERR(eqep->mmio_base))
		return PTR_ERR(eqep->mmio_base);

	/* Store the platform device in our eQEP data structure for later usage */
	eqep->pdev = pdev;

	/* Subscribe to the eQEP interrupt */
	if (request_irq(eqep->irq, eqep_irq_handler, IRQF_IRQPOLL, "eqep_interrupt", pdev))
	{
		dev_err(&pdev->dev, "unable to request irq for eQEP\n");
		return -ENODEV;
	}

	/* Register controls to sysfs */
	if (sysfs_create_group(&pdev->dev.kobj, &eqep_device_attr_group))
	{
		dev_err(&pdev->dev, "sysfs creation failed\n");
		return -EINVAL;
	}

	/* set QDECCTL */
	status = 0;	/* default to Quadrature count mode, QSRC1 & QSRC0 = 0 */

	/* set QSRC1 & QSRC0 bits, one of 4 count_modes. */
	if (!of_property_read_u32(pdev->dev.of_node, "count_mode", &value) && value <= 3) {
		status |= value << 14;

		/*
		 * in count up or count down mode, count on rising edge only
		 * not on both edges.
		 */
		if (value >= 2)
			status |= XCR;
	}
	dev_info(&pdev->dev, "count_mode:%d\n", value);

	/* Should we invert the qa input */
	if (!of_property_read_u32(pdev->dev.of_node, "invert_qa", &value))
		status = value ? status | QAP : status & ~QAP;
	dev_info(&pdev->dev, "invert_qa:%d\n", value);

	/* Should we invert the qb input */
	if (!of_property_read_u32(pdev->dev.of_node, "invert_qb", &value))
		status = value ? status | QBP : status & ~QBP;
	dev_info(&pdev->dev, "invert_qb:%d\n", value);

	/* Should we invert the index input */
	if (!of_property_read_u32(pdev->dev.of_node, "invert_qi", &value))
		status = value ? status | QIP : status & ~QIP;
	dev_info(&pdev->dev, "invert_qi:%d\n", value);

	/* Should we invert the strobe input */
	if (!of_property_read_u32(pdev->dev.of_node, "invert_qs", &value))
		status = value ? status | QSP : status & ~QSP;
	dev_info(&pdev->dev, "invert_qs:%d\n", value);

	/* Should we swap the cha and chb inputs */
	if (!of_property_read_u32(pdev->dev.of_node, "swap_inputs", &value))
		status = value ? status | SWAP : status & ~SWAP;
	dev_info(&pdev->dev, "swap_inputs:%d\n", value);

	dev_info(&pdev->dev, "QDECCTL:0x%04x\n", status);

	/* Write the decoder control settings back to the control register */
	writew(status, eqep->mmio_base + QDECCTL);

	writel( 0, eqep->mmio_base + QPOSINIT);
	writel(~0, eqep->mmio_base + QPOSMAX);
	writel( 0, eqep->mmio_base + QPOSCNT);

	dev_info(&pdev->dev, "QPOSINIT:0x%08x\n", readl(eqep->mmio_base + QPOSINIT));
	dev_info(&pdev->dev, "QPOSMAX:0x%08x\n", readl(eqep->mmio_base + QPOSMAX));
	dev_info(&pdev->dev, "QPOSCNT:0x%08x\n", readl(eqep->mmio_base + QPOSCNT));

	status = UTOF;		/* Enable Unit Time Period interrupt. */
	if (!of_property_read_u32(pdev->dev.of_node, "omit_interrupt", &value) && value) {
		status = 0;	/* no interrupt */
	}
	writew(status, eqep->mmio_base + QEINT);
	dev_info(&pdev->dev, "omit_interrupt:%d\n", value);
	dev_info(&pdev->dev, "QEINT:0x%04x\n", status);

	/* Calculate the timer ticks per second */
	period = 1000000000;
	period = period * eqep->clk_rate;
	do_div(period, NSEC_PER_SEC);

	/* Set this period into the unit timer period register */
	writel(period, eqep->mmio_base + QUPRD);
	dev_info(&pdev->dev, "QUPRD:0x%08x\n", (u32) period);

	/*
	 * Enable the eQEP with basic position counting turned on
	 * PHEN - Quadrature position counter enable bit
	 * UTE	- unit timer enable
	 * QCLM - latch QPOSLAT to QPOSCNT upon unit timer overflow
	 * IEL0 - Latch QPOSILAT on index signal.  Rising or falling, IEL[1:0] = 0 is reserved
	 * SWI	- Software initialization of position count register, i.e. set QPOSCNT <= QPOSINIT,
	 *  but this bit was not being reset by hardware as advertised in TRM,
	 *  (so omit & clear QPOSCNT manually elsewhere?)
	 */
	status = PHEN | UTE | QCLM | IEL0 | SWI;
	writew(status, eqep->mmio_base + QEPCTL);
	dev_info(&pdev->dev, "QEPCTL:0x%04x write\n", status);
	dev_info(&pdev->dev, "QEPCTL:0x%04x read\n", readw(eqep->mmio_base + QEPCTL));

	/* We default to absolute mode */
	eqep->op_mode = TIEQEP_MODE_ABSOLUTE;

	/* Enable the power management runtime */
	pm_runtime_enable(&pdev->dev);

	/* Increment the device usage count and run pm_runtime_resume() */
	pm_runtime_get_sync(&pdev->dev);

	/* Initialize the notify work struture */
	INIT_WORK(&eqep->notify_work, notify_handler);

	/* Decrement the device usage count (twice) and run pm_runtime_idle() if zero */
	pm_runtime_put_sync(&pdev->dev);

	/* Set the platform driver data to the data object we've been creating for the eQEP unit */
	platform_set_drvdata(pdev, eqep);

	/* Success! */
	dev_info(&pdev->dev, "irq:%d, clk_rate:%u\n", eqep->irq, eqep->clk_rate);
	return 0;
}

/* Remove an instance of the eQEP driver */
static int eqep_remove(struct platform_device *pdev)
{
	/* Get the eQEP driver data from the platform device structure */
	struct eqep_chip *eqep = platform_get_drvdata(pdev);

	/* Cancel work */
	cancel_work_sync(&eqep->notify_work);

	/* Unmap from sysfs */
	sysfs_remove_group(&pdev->dev.kobj, &eqep_device_attr_group);

	/* Release important assets */
	free_irq(eqep->irq, pdev);

	/* Increment the device usage count and run pm_runtime_resume() */
	pm_runtime_get_sync(&pdev->dev);

	/* Decrement the device usage count (twice) and run pm_runtime_idle() if zero */
	pm_runtime_put_sync(&pdev->dev);
	pm_runtime_put_sync(&pdev->dev);

	/* Disable the runtime power management of this device */
	pm_runtime_disable(&pdev->dev);

	/* Return success */
	return 0;
}

/* Power management suspend device */
static int eqep_suspend(struct device *dev)
{
	/* Get the eqep driver information */
	struct eqep_chip   *eqep = dev_get_drvdata(dev);
	u16					tmp;

	/* Shut down interrupts */
	eqep->prior_qeint = readw(eqep->mmio_base + QEINT);
	tmp = eqep->prior_qeint & ~UTOF;
	writew(tmp, eqep->mmio_base + QEINT);

	/* Get the existing state of QEPCTL */
	eqep->prior_qepctl = readw(eqep->mmio_base + QEPCTL);

	/* Disable eQEP controller */
	writew(eqep->prior_qepctl & ~PHEN, eqep->mmio_base + QEPCTL);

	/* Decrement the device usage count and run pm_runtime_idle() if zero */
	pm_runtime_put_sync(dev);

	/* Return success */
	return 0;
}

/* Power management wake device back up */
static int eqep_resume(struct device *dev)
{
	/* Get the eqep driver information */
	struct eqep_chip *eqep = dev_get_drvdata(dev);

	/* Restore interrupt enabled register */
	writew(eqep->prior_qeint, eqep->mmio_base + QEINT);

	/* Restore prior qep control register */
	writew(eqep->prior_qepctl, eqep->mmio_base + QEPCTL);

	/* Increment the device usage count and run pm_runtime_resume() */
	pm_runtime_get_sync(dev);

	/* Success */
	return 0;
}

/* create pm functions object */
static SIMPLE_DEV_PM_OPS(eqep_pm_ops, eqep_suspend, eqep_resume);

/* Platform driver information */
static struct platform_driver eqep_driver = {
	.driver = {
		.name	= "eqep",
		.owner	= THIS_MODULE,
		.pm	= &eqep_pm_ops,
		.of_match_table = eqep_of_match,
	},
	.probe = eqep_probe,
	.remove = eqep_remove,
};

/* Register this platform driver */
module_platform_driver(eqep_driver);

/* Module information */
MODULE_DESCRIPTION("TI eQEP driver");
MODULE_AUTHOR("Nathaniel R. Lewis");
MODULE_LICENSE("GPL");

Attachment: am33xx.dtsi
Description: Binary data

Reply via email to