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");
am33xx.dtsi
Description: Binary data
