Commit 9957679f authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

bring back xdevcfg

parent bf5a43ac
# SPDX-License-Identifier: GPL-2.0
#
# Character device configuration
#
menu "Character devices"
source "drivers/tty/Kconfig"
config DEVMEM
bool "/dev/mem virtual device support"
default y
help
Say Y here if you want to support the /dev/mem device.
The /dev/mem device is used to access areas of physical
memory.
When in doubt, say "Y".
config DEVKMEM
bool "/dev/kmem virtual device support"
# On arm64, VMALLOC_START < PAGE_OFFSET, which confuses kmem read/write
depends on !ARM64
help
Say Y here if you want to support the /dev/kmem device. The
/dev/kmem device is rarely used, but can be used for certain
kind of kernel debugging operations.
When in doubt, say "N".
config SGI_SNSC
bool "SGI Altix system controller communication support"
depends on (IA64_SGI_SN2 || IA64_GENERIC)
help
If you have an SGI Altix and you want to enable system
controller communication from user space (you want this!),
say Y. Otherwise, say N.
config SGI_TIOCX
bool "SGI TIO CX driver support"
depends on (IA64_SGI_SN2 || IA64_GENERIC)
help
If you have an SGI Altix and you have fpga devices attached
to your TIO, say Y here, otherwise say N.
config SGI_MBCS
tristate "SGI FPGA Core Services driver support"
depends on SGI_TIOCX
help
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
source "drivers/tty/serial/Kconfig"
source "drivers/tty/serdev/Kconfig"
config TTY_PRINTK
tristate "TTY driver to output user messages via printk"
depends on EXPERT && TTY
default n
---help---
If you say Y here, the support for writing user messages (i.e.
console messages) via printk is available.
The feature is useful to inline user messages with kernel
messages.
In order to use this feature, you should output user messages
to /dev/ttyprintk or redirect console to this TTY.
If unsure, say N.
config BFIN_OTP
tristate "Blackfin On-Chip OTP Memory Support"
depends on BLACKFIN && (BF51x || BF52x || BF54x)
default y
help
If you say Y here, you will get support for a character device
interface into the One Time Programmable memory pages that are
stored on the Blackfin processor. This will not get you access
to the secure memory pages however. You will need to write your
own secure code and reader for that.
To compile this driver as a module, choose M here: the module
will be called bfin-otp.
If unsure, it is safe to say Y.
config BFIN_OTP_WRITE_ENABLE
bool "Enable writing support of OTP pages"
depends on BFIN_OTP
default n
help
If you say Y here, you will enable support for writing of the
OTP pages. This is dangerous by nature as you can only program
the pages once, so only enable this option when you actually
need it so as to not inadvertently clobber data.
If unsure, say N.
config PRINTER
tristate "Parallel printer support"
depends on PARPORT
---help---
If you intend to attach a printer to the parallel port of your Linux
box (as opposed to using a serial printer; if the connector at the
printer has 9 or 25 holes ["female"], then it's serial), say Y.
Also read the Printing-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
It is possible to share one parallel port among several devices
(e.g. printer and ZIP drive) and it is safe to compile the
corresponding drivers into the kernel.
To compile this driver as a module, choose M here and read
<file:Documentation/parport.txt>. The module will be called lp.
If you have several parallel ports, you can specify which ports to
use with the "lp" kernel command line option. (Try "man bootparam"
or see the documentation of your boot loader (lilo or loadlin) about
how to pass options to the kernel at boot time.) The syntax of the
"lp" command line option can be found in <file:drivers/char/lp.c>.
If you have more than 8 printers, you need to increase the LP_NO
macro in lp.c and the PARPORT_MAX macro in parport.h.
config LP_CONSOLE
bool "Support for console on line printer"
depends on PRINTER
---help---
If you want kernel messages to be printed out as they occur, you
can have a console on the printer. This option adds support for
doing that; to actually get it to happen you need to pass the
option "console=lp0" to the kernel at boot time.
If the printer is out of paper (or off, or unplugged, or too
busy..) the kernel will stall until the printer is ready again.
By defining CONSOLE_LP_STRICT to 0 (at your own risk) you
can make the kernel continue when this happens,
but it'll lose the kernel messages.
If unsure, say N.
config PPDEV
tristate "Support for user-space parallel port device drivers"
depends on PARPORT
---help---
Saying Y to this adds support for /dev/parport device nodes. This
is needed for programs that want portable access to the parallel
port, for instance deviceid (which displays Plug-and-Play device
IDs).
This is the parallel port equivalent of SCSI generic support (sg).
It is safe to say N to this -- it is not needed for normal printing
or parallel port CD-ROM/disk support.
To compile this driver as a module, choose M here: the
module will be called ppdev.
If unsure, say N.
source "drivers/tty/hvc/Kconfig"
config VIRTIO_CONSOLE
tristate "Virtio console"
depends on VIRTIO && TTY
select HVC_DRIVER
help
Virtio console for use with hypervisors.
Also serves as a general-purpose serial device for data
transfer between the guest and host. Character devices at
/dev/vportNpn will be created when corresponding ports are
found, where N is the device number and n is the port number
within that device. If specified by the host, a sysfs
attribute called 'name' will be populated with a name for
the port which can be used by udev scripts to create a
symlink to the device.
config IBM_BSR
tristate "IBM POWER Barrier Synchronization Register support"
depends on PPC_PSERIES
help
This devices exposes a hardware mechanism for fast synchronization
of threads across a large system which avoids bouncing a cacheline
between several cores on a system
config POWERNV_OP_PANEL
tristate "IBM POWERNV Operator Panel Display support"
depends on PPC_POWERNV
default m
help
If you say Y here, a special character device node, /dev/op_panel,
will be created which exposes the operator panel display on IBM
Power Systems machines with FSPs.
If you don't require access to the operator panel display from user
space, say N.
If unsure, say M here to build it as a module called powernv-op-panel.
source "drivers/char/ipmi/Kconfig"
config DS1620
tristate "NetWinder thermometer support"
depends on ARCH_NETWINDER
help
Say Y here to include support for the thermal management hardware
found in the NetWinder. This driver allows the user to control the
temperature set points and to read the current temperature.
It is also possible to say M here to build it as a module (ds1620)
It is recommended to be used on a NetWinder, but it is not a
necessity.
config NWBUTTON
tristate "NetWinder Button"
depends on ARCH_NETWINDER
---help---
If you say Y here and create a character device node /dev/nwbutton
with major and minor numbers 10 and 158 ("man mknod"), then every
time the orange button is pressed a number of times, the number of
times the button was pressed will be written to that device.
This is most useful for applications, as yet unwritten, which
perform actions based on how many times the button is pressed in a
row.
Do not hold the button down for too long, as the driver does not
alter the behaviour of the hardware reset circuitry attached to the
button; it will still execute a hard reset if the button is held
down for longer than approximately five seconds.
To compile this driver as a module, choose M here: the
module will be called nwbutton.
Most people will answer Y to this question and "Reboot Using Button"
below to be able to initiate a system shutdown from the button.
config NWBUTTON_REBOOT
bool "Reboot Using Button"
depends on NWBUTTON
help
If you say Y here, then you will be able to initiate a system
shutdown and reboot by pressing the orange button a number of times.
The number of presses to initiate the shutdown is two by default,
but this can be altered by modifying the value of NUM_PRESSES_REBOOT
in nwbutton.h and recompiling the driver or, if you compile the
driver as a module, you can specify the number of presses at load
time with "insmod button reboot_count=<something>".
config NWFLASH
tristate "NetWinder flash support"
depends on ARCH_NETWINDER
---help---
If you say Y here and create a character device /dev/flash with
major 10 and minor 160 you can manipulate the flash ROM containing
the NetWinder firmware. Be careful as accidentally overwriting the
flash contents can render your computer unbootable. On no account
allow random users access to this device. :-)
To compile this driver as a module, choose M here: the
module will be called nwflash.
If you're not sure, say N.
source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM
---help---
If you say Y here and create a character special file /dev/nvram
with major number 10 and minor number 144 using mknod ("man mknod"),
you get read and write access to the extra bytes of non-volatile
memory in the real time clock (RTC), which is contained in every PC
and most Ataris. The actual number of bytes varies, depending on the
nvram in the system, but is usually 114 (128-14 for the RTC).
This memory is conventionally called "CMOS RAM" on PCs and "NVRAM"
on Ataris. /dev/nvram may be used to view settings there, or to
change them (with some utility). It could also be used to frequently
save a few bits of very important data that may not be lost over
power-off and for which writing to disk is too insecure. Note
however that most NVRAM space in a PC belongs to the BIOS and you
should NEVER idly tamper with it. See Ralf Brown's interrupt list
for a guide to the use of CMOS bytes by your BIOS.
On Atari machines, /dev/nvram is always configured and does not need
to be selected.
To compile this driver as a module, choose M here: the
module will be called nvram.
#
# These legacy RTC drivers just cause too many conflicts with the generic
# RTC framework ... let's not even try to coexist any more.
#
if RTC_LIB=n
config RTC
tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
depends on ALPHA || (MIPS && MACH_LOONGSON64)
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
will get access to the real time clock (or hardware clock) built
into your computer.
Every PC has such a clock built in. It can be used to generate
signals from as low as 1Hz up to 8192Hz, and can also be used
as a 24 hour alarm. It reports status information via the file
/proc/driver/rtc and its behaviour is set by various ioctls on
/dev/rtc.
If you run Linux on a multiprocessor machine and said Y to
"Symmetric Multi Processing" above, you should say Y here to read
and set the RTC in an SMP compatible fashion.
If you think you have a use for such a device (such as periodic data
sampling), then say Y here, and read <file:Documentation/rtc.txt>
for details.
To compile this driver as a module, choose M here: the
module will be called rtc.
config JS_RTC
tristate "Enhanced Real Time Clock Support"
depends on SPARC32 && PCI
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
will get access to the real time clock (or hardware clock) built
into your computer.
Every PC has such a clock built in. It can be used to generate
signals from as low as 1Hz up to 8192Hz, and can also be used
as a 24 hour alarm. It reports status information via the file
/proc/driver/rtc and its behaviour is set by various ioctls on
/dev/rtc.
If you think you have a use for such a device (such as periodic data
sampling), then say Y here, and read <file:Documentation/rtc.txt>
for details.
To compile this driver as a module, choose M here: the
module will be called js-rtc.
config EFI_RTC
bool "EFI Real Time Clock Services"
depends on IA64
config DS1302
tristate "DS1302 RTC support"
depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT)
help
If you say Y here and create a character special file /dev/rtc with
major number 121 and minor number 0 using mknod ("man mknod"), you
will get access to the real time clock (or hardware clock) built
into your computer.
endif # RTC_LIB
config DTLK
tristate "Double Talk PC internal speech card support"
depends on ISA
help
This driver is for the DoubleTalk PC, a speech synthesizer
manufactured by RC Systems (<http://www.rcsys.com/>). It is also
called the `internal DoubleTalk'.
To compile this driver as a module, choose M here: the
module will be called dtlk.
config XILINX_HWICAP
tristate "Xilinx HWICAP Support"
depends on XILINX_VIRTEX || MICROBLAZE
help
This option enables support for Xilinx Internal Configuration
Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex
FPGA platforms to partially reconfigure the FPGA at runtime.
If unsure, say N.
config XILINX_DEVCFG
tristate "Xilinx Device Configuration"
depends on ARCH_ZYNQ
help
This option enables support for the Xilinx device configuration driver.
If unsure, say N
config R3964
tristate "Siemens R3964 line discipline"
depends on TTY
---help---
This driver allows synchronous communication with devices using the
Siemens R3964 packet protocol. Unless you are dealing with special
hardware like PLCs, you are unlikely to need this.
To compile this driver as a module, choose M here: the
module will be called n_r3964.
If unsure, say N.
config APPLICOM
tristate "Applicom intelligent fieldbus card support"
depends on PCI
---help---
This driver provides the kernel-side support for the intelligent
fieldbus cards made by Applicom International. More information
about these cards can be found on the WWW at the address
<http://www.applicom-int.com/>, or by email from David Woodhouse
<dwmw2@infradead.org>.
To compile this driver as a module, choose M here: the
module will be called applicom.
If unsure, say N.
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
depends on X86_32 && PCI && INPUT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
If you have one of those laptops, read
<file:Documentation/laptops/sonypi.txt>, and say Y or M here.
To compile this driver as a module, choose M here: the
module will be called sonypi.
config GPIO_TB0219
tristate "TANBAC TB0219 GPIO support"
depends on TANBAC_TB022X
select GPIO_VR41XX
source "drivers/char/pcmcia/Kconfig"
config MWAVE
tristate "ACP Modem (Mwave) support"
depends on X86 && TTY
select SERIAL_8250
---help---
The ACP modem (Mwave) for Linux is a WinModem. It is composed of a
kernel driver and a user level application. Together these components
support direct attachment to public switched telephone networks (PSTNs)
and support selected world wide countries.
This version of the ACP Modem driver supports the IBM Thinkpad 600E,
600, and 770 that include on board ACP modem hardware.
The modem also supports the standard communications port interface
(ttySx) and is compatible with the Hayes AT Command Set.
The user level application needed to use this driver can be found at
the IBM Linux Technology Center (LTC) web site:
<http://www.ibm.com/linux/ltc/>.
If you own one of the above IBM Thinkpads which has the Mwave chipset
in it, say Y.
To compile this driver as a module, choose M here: the
module will be called mwave.
config SCx200_GPIO
tristate "NatSemi SCx200 GPIO Support"
depends on SCx200
select NSC_GPIO
help
Give userspace access to the GPIO pins on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_gpio.
config PC8736x_GPIO
tristate "NatSemi PC8736x GPIO Support"
depends on X86_32 && !UML
default SCx200_GPIO # mostly N
select NSC_GPIO # needed for support routines
help
Give userspace access to the GPIO pins on the National
Semiconductor PC-8736x (x=[03456]) SuperIO chip. The chip
has multiple functional units, inc several managed by
hwmon/pc87360 driver. Tested with PC-87366
If compiled as a module, it will be called pc8736x_gpio.
config NSC_GPIO
tristate "NatSemi Base GPIO Support"
depends on X86_32
# selected by SCx200_GPIO and PC8736x_GPIO
# what about 2 selectors differing: m != y
help
Common support used (and needed) by scx200_gpio and
pc8736x_gpio drivers. If those drivers are built as
modules, this one will be too, named nsc_gpio
config RAW_DRIVER
tristate "RAW driver (/dev/raw/rawN)"
depends on BLOCK
help
The raw driver permits block devices to be bound to /dev/raw/rawN.
Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
See the raw(8) manpage for more details.
Applications should preferably open the device (eg /dev/hda1)
with the O_DIRECT flag.
config MAX_RAW_DEVS
int "Maximum number of RAW devices to support (1-65536)"
depends on RAW_DRIVER
range 1 65536
default "256"
help
The maximum number of RAW devices that are supported.
Default is 256. Increase this number in case you need lots of
raw devices.
config HPET
bool "HPET - High Precision Event Timer" if (X86 || IA64)
default n
depends on ACPI
help
If you say Y here, you will have a miscdevice named "/dev/hpet/". Each
open selects one of the timers supported by the HPET. The timers are
non-periodic and/or periodic.
config HPET_MMAP
bool "Allow mmap of HPET"
default y
depends on HPET
help
If you say Y here, user applications will be able to mmap
the HPET registers.
config HPET_MMAP_DEFAULT
bool "Enable HPET MMAP access by default"
default y
depends on HPET_MMAP
help
In some hardware implementations, the page containing HPET
registers may also contain other things that shouldn't be
exposed to the user. This option selects the default (if
kernel parameter hpet_mmap is not set) user access to the
registers for applications that require it.
config HANGCHECK_TIMER
tristate "Hangcheck timer"
depends on X86 || IA64 || PPC64 || S390
help
The hangcheck-timer module detects when the system has gone
out to lunch past a certain margin. It can reboot the system
or merely print a warning.
config UV_MMTIMER
tristate "UV_MMTIMER Memory mapped RTC for SGI UV"
depends on X86_UV
default m
help
The uv_mmtimer device allows direct userspace access to the
UV system timer.
source "drivers/char/tpm/Kconfig"
config TELCLOCK
tristate "Telecom clock driver for ATCA SBC"
depends on X86
default n
help
The telecom clock device is specific to the MPCBL0010 and MPCBL0050
ATCA computers and allows direct userspace access to the
configuration of the telecom clock configuration settings. This
device is used for hardware synchronization across the ATCA backplane
fabric. Upon loading, the driver exports a sysfs directory,
/sys/devices/platform/telco_clock, with a number of files for
controlling the behavior of this hardware.
config DEVPORT
bool "/dev/port character device"
depends on ISA || PCI
default y
help
Say Y here if you want to support the /dev/port device. The /dev/port
device is similar to /dev/mem, but for I/O ports.
source "drivers/s390/char/Kconfig"
config TILE_SROM
tristate "Character-device access via hypervisor to the Tilera SPI ROM"
depends on TILE
default y
---help---
This device provides character-level read-write access
to the SROM, typically via the "0", "1", and "2" devices
in /dev/srom/. The Tilera hypervisor makes the flash
device appear much like a simple EEPROM, and knows
how to partition a single ROM for multiple purposes.
source "drivers/char/xillybus/Kconfig"
endmenu
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the kernel character device drivers.
#
obj-y += mem.o random.o
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
obj-$(CONFIG_IBM_BSR) += bsr.o
obj-$(CONFIG_SGI_MBCS) += mbcs.o
obj-$(CONFIG_BFIN_OTP) += bfin-otp.o
obj-$(CONFIG_PRINTER) += lp.o
obj-$(CONFIG_APM_EMULATION) += apm-emulation.o
obj-$(CONFIG_DTLK) += dtlk.o
obj-$(CONFIG_APPLICOM) += applicom.o
obj-$(CONFIG_SONYPI) += sonypi.o
obj-$(CONFIG_RTC) += rtc.o
obj-$(CONFIG_HPET) += hpet.o
obj-$(CONFIG_EFI_RTC) += efirtc.o
obj-$(CONFIG_DS1302) += ds1302.o
obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/
obj-$(CONFIG_XILINX_DEVCFG) += xilinx_devcfg.o
ifeq ($(CONFIG_GENERIC_NVRAM),y)
obj-$(CONFIG_NVRAM) += generic_nvram.o
else
obj-$(CONFIG_NVRAM) += nvram.o
endif
obj-$(CONFIG_TOSHIBA) += toshiba.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_HW_RANDOM) += hw_random/
obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
obj-$(CONFIG_MWAVE) += mwave/
obj-y += agp/
obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
/*
* Xilinx Zynq Device Config driver
*
* Copyright (c) 2011 - 2013 Xilinx Inc.
*
* 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.
*/
#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <../../arch/arm/mach-zynq/common.h>
#define DRIVER_NAME "xdevcfg"
#define XDEVCFG_DEVICES 1
/* An array, which is set to true when the device is registered. */
static DEFINE_MUTEX(xdevcfg_mutex);
/* Constant Definitions */
#define XDCFG_CTRL_OFFSET 0x00 /* Control Register */
#define XDCFG_LOCK_OFFSET 0x04 /* Lock Register */
#define XDCFG_INT_STS_OFFSET 0x0C /* Interrupt Status Register */
#define XDCFG_INT_MASK_OFFSET 0x10 /* Interrupt Mask Register */
#define XDCFG_STATUS_OFFSET 0x14 /* Status Register */
#define XDCFG_DMA_SRC_ADDR_OFFSET 0x18 /* DMA Source Address Register */
#define XDCFG_DMA_DEST_ADDR_OFFSET 0x1C /* DMA Destination Address Reg */
#define XDCFG_DMA_SRC_LEN_OFFSET 0x20 /* DMA Source Transfer Length */
#define XDCFG_DMA_DEST_LEN_OFFSET 0x24 /* DMA Destination Transfer */
#define XDCFG_UNLOCK_OFFSET 0x34 /* Unlock Register */
#define XDCFG_MCTRL_OFFSET 0x80 /* Misc. Control Register */
/* Control Register Bit definitions */
#define XDCFG_CTRL_PCFG_PROG_B_MASK 0x40000000 /* Program signal to
* Reset FPGA
*/
#define XDCFG_CTRL_PCAP_PR_MASK 0x08000000 /* Enable PCAP for PR */
#define XDCFG_CTRL_PCAP_MODE_MASK 0x04000000 /* Enable PCAP */
#define XDCFG_CTRL_PCAP_RATE_EN_MASK 0x02000000 /* Enable PCAP Quad Rate */
#define XDCFG_CTRL_PCFG_AES_EN_MASK 0x00000E00 /* AES Enable Mask */
#define XDCFG_CTRL_SEU_EN_MASK 0x00000100 /* SEU Enable Mask */
#define XDCFG_CTRL_SPNIDEN_MASK 0x00000040 /* Secure Non Invasive
* Debug Enable
*/
#define XDCFG_CTRL_SPIDEN_MASK 0x00000020 /* Secure Invasive
* Debug Enable
*/
#define XDCFG_CTRL_NIDEN_MASK 0x00000010 /* Non-Invasive Debug
* Enable
*/
#define XDCFG_CTRL_DBGEN_MASK 0x00000008 /* Invasive Debug
* Enable
*/
#define XDCFG_CTRL_DAP_EN_MASK 0x00000007 /* DAP Enable Mask */
/* Lock register bit definitions */
#define XDCFG_LOCK_AES_EN_MASK 0x00000008 /* Lock AES_EN update */
#define XDCFG_LOCK_SEU_MASK 0x00000004 /* Lock SEU_En update */
#define XDCFG_LOCK_DBG_MASK 0x00000001 /* This bit locks
* security config
* including: DAP_En,
* DBGEN,NIDEN, SPNIEN
*/
/* Miscellaneous Control Register bit definitions */
#define XDCFG_MCTRL_PCAP_LPBK_MASK 0x00000010 /* Internal PCAP loopback */
/* Status register bit definitions */
#define XDCFG_STATUS_PCFG_INIT_MASK 0x00000010 /* FPGA init status */
/* Interrupt Status/Mask Register Bit definitions */
#define XDCFG_IXR_DMA_DONE_MASK 0x00002000 /* DMA Command Done */
#define XDCFG_IXR_D_P_DONE_MASK 0x00001000 /* DMA and PCAP Cmd Done */
#define XDCFG_IXR_PCFG_DONE_MASK 0x00000004 /* FPGA programmed */
#define XDCFG_IXR_ERROR_FLAGS_MASK 0x00F0F860
#define XDCFG_IXR_ALL_MASK 0xF8F7F87F
/* Miscellaneous constant values */
#define XDCFG_DMA_INVALID_ADDRESS 0xFFFFFFFF /* Invalid DMA address */
static const char * const fclk_name[] = {
"fclk0",
"fclk1",
"fclk2",
"fclk3"
};
#define NUMFCLKS ARRAY_SIZE(fclk_name)
/**
* struct xdevcfg_drvdata - Device Configuration driver structure
*
* @dev: Pointer to the device structure
* @cdev: Instance of the cdev structure
* @devt: Pointer to the dev_t structure
* @class: Pointer to device class
* @fclk_class: Pointer to fclk device class
* @dma_done: The dma_done status bit for the DMA command completion
* @error_status: The error status captured during the DMA transfer
* @irq: Interrupt number
* @clk: Peripheral clock for devcfg
* @fclk: Array holding references to the FPGA clocks
* @fclk_exported: Flag inidcating whether an FPGA clock is exported
* @is_open: The status bit to indicate whether the device is opened
* @sem: Instance for the mutex
* @lock: Instance of spinlock
* @base_address: The virtual device base address of the device registers
* @ep107: Flags is used to identify the platform
* @endian_swap: Flags is used to identify the endianness format
* @residue_buf: Array holding stragglers from last time (0 to 3 bytes)
* @residue_len: stragglers length in bytes
* @is_partial_bitstream: Status bit to indicate partial/full bitstream
*/
struct xdevcfg_drvdata {
struct device *dev;
struct cdev cdev;
dev_t devt;
struct class *class;
struct class *fclk_class;
int irq;
struct clk *clk;
struct clk *fclk[NUMFCLKS];
u8 fclk_exported[NUMFCLKS];
bool dma_done;
int error_status;
bool is_open;
struct mutex sem;
spinlock_t lock;
void __iomem *base_address;
int ep107;
bool is_partial_bitstream;
bool endian_swap;
char residue_buf[3];
int residue_len;
};
/**
* struct fclk_data - FPGA clock data
* @clk: Pointer to clock
* @enabled: Flag indicating enable status of the clock
* @rate_rnd: Rate to be rounded for round rate operation
*/
struct fclk_data {
struct clk *clk;
int enabled;
unsigned long rate_rnd;
};
/* Register read/write access routines */
#define xdevcfg_writereg(offset, val) __raw_writel(val, offset)
#define xdevcfg_readreg(offset) __raw_readl(offset)
/**
* xdevcfg_reset_pl - Reset the programmable logic.
* @base_address: The base address of the device.
*
* Must be called with PCAP clock enabled
*/
static void xdevcfg_reset_pl(void __iomem *base_address)
{
/*
* Create a rising edge on PCFG_INIT. PCFG_INIT follows PCFG_PROG_B,
* so we need to * poll it after setting PCFG_PROG_B to make sure that
* the rising edge happens.
*/
xdevcfg_writereg(base_address + XDCFG_CTRL_OFFSET,
(xdevcfg_readreg(base_address + XDCFG_CTRL_OFFSET) &
~XDCFG_CTRL_PCFG_PROG_B_MASK));
while (xdevcfg_readreg(base_address + XDCFG_STATUS_OFFSET) &
XDCFG_STATUS_PCFG_INIT_MASK)
;
usleep_range(5000, 5100);
xdevcfg_writereg(base_address + XDCFG_CTRL_OFFSET,
(xdevcfg_readreg(base_address + XDCFG_CTRL_OFFSET) |
XDCFG_CTRL_PCFG_PROG_B_MASK));
while (!(xdevcfg_readreg(base_address + XDCFG_STATUS_OFFSET) &
XDCFG_STATUS_PCFG_INIT_MASK))
;
}
/**
* xdevcfg_irq - The main interrupt handler.
* @irq: The interrupt number.
* @data: Pointer to the driver data structure.
* returns: IRQ_HANDLED after the interrupt is handled.
**/
static irqreturn_t xdevcfg_irq(int irq, void *data)
{
u32 intr_status;
struct xdevcfg_drvdata *drvdata = data;
spin_lock(&drvdata->lock);
intr_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_INT_STS_OFFSET);
/* Clear the interrupts */
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET,
intr_status);
if ((intr_status & XDCFG_IXR_D_P_DONE_MASK) ==
XDCFG_IXR_D_P_DONE_MASK)
drvdata->dma_done = 1;
if ((intr_status & XDCFG_IXR_ERROR_FLAGS_MASK) ==
XDCFG_IXR_ERROR_FLAGS_MASK)
drvdata->error_status = 1;
spin_unlock(&drvdata->lock);
return IRQ_HANDLED;
}
/**
* xdevcfg_write - The is the driver write function.
*
* @file: Pointer to the file structure.
* @buf: Pointer to the bitstream location.
* @count: The number of bytes to be written.
* @ppos: Pointer to the offset value
* returns: Success or error status.
**/
static ssize_t
xdevcfg_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos)
{
char *kbuf;
int status;
unsigned long timeout;
u32 intr_reg, dma_len;
dma_addr_t dma_addr;
u32 transfer_length = 0;
struct xdevcfg_drvdata *drvdata = file->private_data;
size_t user_count = count;
int i;
status = clk_enable(drvdata->clk);
if (status)
return status;
status = mutex_lock_interruptible(&drvdata->sem);
if (status)
goto err_clk;
dma_len = count + drvdata->residue_len;
kbuf = dma_alloc_coherent(drvdata->dev, dma_len, &dma_addr, GFP_KERNEL);
if (!kbuf) {
status = -ENOMEM;
goto err_unlock;
}
/* Collect stragglers from last time (0 to 3 bytes) */
memcpy(kbuf, drvdata->residue_buf, drvdata->residue_len);
/* Fetch user data, appending to stragglers */
if (copy_from_user(kbuf + drvdata->residue_len, buf, count)) {
status = -EFAULT;
goto error;
}
/* Include stragglers in total bytes to be handled */
count += drvdata->residue_len;
/* First block contains a header */
if (*ppos == 0 && count > 4) {
/* Look for sync word */
for (i = 0; i < count - 4; i++) {
if (memcmp(kbuf + i, "\x66\x55\x99\xAA", 4) == 0) {
pr_debug("Found normal sync word\n");
drvdata->endian_swap = 0;
break;
}
if (memcmp(kbuf + i, "\xAA\x99\x55\x66", 4) == 0) {
pr_debug("Found swapped sync word\n");
drvdata->endian_swap = 1;
break;
}
}
/* Remove the header, aligning the data on word boundary */
if (i != count - 4) {
count -= i;
memmove(kbuf, kbuf + i, count);
}
}
/* Save stragglers for next time */
drvdata->residue_len = count % 4;
count -= drvdata->residue_len;
memcpy(drvdata->residue_buf, kbuf + count, drvdata->residue_len);
/* Fixup endianess of the data */
if (drvdata->endian_swap) {
for (i = 0; i < count; i += 4) {
u32 *p = (u32 *)&kbuf[i];
*p = swab32(*p);
}
}
/* Enable DMA and error interrupts */
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET,
XDCFG_IXR_ALL_MASK);
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET,
(u32) (~(XDCFG_IXR_D_P_DONE_MASK |
XDCFG_IXR_ERROR_FLAGS_MASK)));
drvdata->dma_done = 0;
drvdata->error_status = 0;
/* Initiate DMA write command */
if (count < 0x1000)
xdevcfg_writereg(drvdata->base_address +
XDCFG_DMA_SRC_ADDR_OFFSET, (u32)(dma_addr + 1));
else
xdevcfg_writereg(drvdata->base_address +
XDCFG_DMA_SRC_ADDR_OFFSET, (u32) dma_addr);
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_ADDR_OFFSET,
(u32)XDCFG_DMA_INVALID_ADDRESS);
/* Convert number of bytes to number of words. */
if (count % 4)
transfer_length = (count / 4 + 1);
else
transfer_length = count / 4;
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_LEN_OFFSET,
transfer_length);
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_LEN_OFFSET, 0);
timeout = jiffies + msecs_to_jiffies(1000);
while (!READ_ONCE(drvdata->dma_done)) {
if (time_after(jiffies, timeout)) {
status = -ETIMEDOUT;
goto error;
}
}
if (READ_ONCE(drvdata->error_status))
status = drvdata->error_status;
/* Disable the DMA and error interrupts */
intr_reg = xdevcfg_readreg(drvdata->base_address +
XDCFG_INT_MASK_OFFSET);
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET,
intr_reg | (XDCFG_IXR_D_P_DONE_MASK |
XDCFG_IXR_ERROR_FLAGS_MASK));
/* If we didn't write correctly, then bail out. */
if (status) {
status = -EFAULT;
goto error;
}
*ppos += user_count;
status = user_count;
error:
dma_free_coherent(drvdata->dev, dma_len, kbuf, dma_addr);
err_unlock:
mutex_unlock(&drvdata->sem);
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_read - The is the driver read function.
* @file: Pointer to the file structure.
* @buf: Pointer to the bitstream location.
* @count: The number of bytes read.
* @ppos: Pointer to the offsetvalue
* returns: Success or error status.
*/
static ssize_t
xdevcfg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
u32 *kbuf;
int status;
unsigned long timeout;
dma_addr_t dma_addr;
struct xdevcfg_drvdata *drvdata = file->private_data;
u32 intr_reg;
status = clk_enable(drvdata->clk);
if (status)
return status;
status = mutex_lock_interruptible(&drvdata->sem);
if (status)
goto err_clk;
/* Get new data from the ICAP, and return was requested. */
kbuf = dma_alloc_coherent(drvdata->dev, count, &dma_addr, GFP_KERNEL);
if (!kbuf) {
status = -ENOMEM;
goto err_unlock;
}
drvdata->dma_done = 0;
drvdata->error_status = 0;
/* Enable DMA and error interrupts */
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET,
XDCFG_IXR_ALL_MASK);
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET,
(u32) (~(XDCFG_IXR_D_P_DONE_MASK |
XDCFG_IXR_ERROR_FLAGS_MASK)));
/* Initiate DMA read command */
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_ADDR_OFFSET,
(u32)XDCFG_DMA_INVALID_ADDRESS);
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_ADDR_OFFSET,
(u32)dma_addr);
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_LEN_OFFSET, 0);
xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_LEN_OFFSET,
count / 4);
timeout = jiffies + msecs_to_jiffies(1000);
while (!drvdata->dma_done) {
if (time_after(jiffies, timeout)) {
status = -ETIMEDOUT;
goto error;
}
}
if (drvdata->error_status)
status = drvdata->error_status;
/* Disable and clear DMA and error interrupts */
intr_reg = xdevcfg_readreg(drvdata->base_address +
XDCFG_INT_MASK_OFFSET);
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET,
intr_reg | (XDCFG_IXR_D_P_DONE_MASK |
XDCFG_IXR_ERROR_FLAGS_MASK));
/* If we didn't read correctly, then bail out. */
if (status) {
status = -EFAULT;
goto error;
}
/* If we fail to return the data to the user, then bail out. */
if (copy_to_user(buf, kbuf, count)) {
status = -EFAULT;
goto error;
}
status = count;
error:
dma_free_coherent(drvdata->dev, count, kbuf, dma_addr);
err_unlock:
mutex_unlock(&drvdata->sem);
err_clk:
clk_disable(drvdata->clk);
return status;
}
static void xdevcfg_enable_partial(struct xdevcfg_drvdata *drvdata)
{
u32 reg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET);
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
reg | XDCFG_CTRL_PCAP_PR_MASK);
}
static void xdevcfg_disable_partial(struct xdevcfg_drvdata *drvdata)
{
u32 reg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET);
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
reg & ~XDCFG_CTRL_PCAP_PR_MASK);
}
/**
* xdevcfg_open - The is the driver open function.
* @inode: Pointer to the inode structure of this device.
* @file: Pointer to the file structure.
* returns: Success or error status.
*/
static int xdevcfg_open(struct inode *inode, struct file *file)
{
struct xdevcfg_drvdata *drvdata;
int status;
drvdata = container_of(inode->i_cdev, struct xdevcfg_drvdata, cdev);
status = clk_enable(drvdata->clk);
if (status)
return status;
status = mutex_lock_interruptible(&drvdata->sem);
if (status)
goto err_clk;
if (drvdata->is_open) {
status = -EBUSY;
goto error;
}
file->private_data = drvdata;
drvdata->is_open = 1;
drvdata->endian_swap = 0;
drvdata->residue_len = 0;
/*
* If is_partial_bitstream is set, then PROG_B is not asserted
* (xdevcfg_reset_pl function) and also zynq_slcr_init_preload_fpga and
* zynq_slcr_init_postload_fpga functions are not invoked.
*/
if (drvdata->is_partial_bitstream)
xdevcfg_enable_partial(drvdata);
else
zynq_slcr_init_preload_fpga();
/*
* Only do the reset of the PL for Zynq as it causes problems on the
* EP107 and the issue is not understood, but not worth investigating
* as the emulation platform is very different than silicon and not a
* complete implementation. Also, do not reset if it is a partial
* bitstream.
*/
if ((!drvdata->ep107) && (!drvdata->is_partial_bitstream))
xdevcfg_reset_pl(drvdata->base_address);
xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET,
XDCFG_IXR_PCFG_DONE_MASK);
error:
mutex_unlock(&drvdata->sem);
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_release - The is the driver release function.
* @inode: Pointer to the inode structure of this device.
* @file: Pointer to the file structure.
* returns: Success.
*/
static int xdevcfg_release(struct inode *inode, struct file *file)
{
struct xdevcfg_drvdata *drvdata = file->private_data;
if (drvdata->is_partial_bitstream)
xdevcfg_disable_partial(drvdata);
else
zynq_slcr_init_postload_fpga();
if (drvdata->residue_len)
dev_info(drvdata->dev, "Did not transfer last %d bytes\n",
drvdata->residue_len);
drvdata->is_open = 0;
return 0;
}
static const struct file_operations xdevcfg_fops = {
.owner = THIS_MODULE,
.write = xdevcfg_write,
.read = xdevcfg_read,
.open = xdevcfg_open,
.release = xdevcfg_release,
};
/*
* The following functions are the routines provided to the user to
* set/get the status bit value in the control/lock registers.
*/
/**
* xdevcfg_set_dap_en - This function sets the DAP bits in the
* control register with the given value.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: negative error if the string could not be converted
* or the size of the buffer.
*/
static ssize_t xdevcfg_set_dap_en(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
spin_lock_irqsave(&drvdata->lock, flags);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_unlock;
if (mask_bit > 7) {
status = -EINVAL;
goto err_unlock;
}
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status |
(((u32)mask_bit) & XDCFG_CTRL_DAP_EN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_unlock:
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_dap_en_status - The function returns the DAP_EN bits status in
* the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: Size of the buffer.
*/
static ssize_t xdevcfg_show_dap_en_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 dap_en_status;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
dap_en_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_DAP_EN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", dap_en_status);
return status;
}
static DEVICE_ATTR(enable_dap, 0644, xdevcfg_show_dap_en_status,
xdevcfg_set_dap_en);
/**
* xdevcfg_set_dbgen - This function sets the DBGEN bit in the
* control register with the given value.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_dbgen(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status | XDCFG_CTRL_DBGEN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status & (~XDCFG_CTRL_DBGEN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_dbgen_status - The function returns the DBGEN bit status in
* the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: Size of the buffer.
*/
static ssize_t xdevcfg_show_dbgen_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 dbgen_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
dbgen_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_DBGEN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (dbgen_status >> 3));
return status;
}
static DEVICE_ATTR(enable_dbg_in, 0644, xdevcfg_show_dbgen_status,
xdevcfg_set_dbgen);
/**
* xdevcfg_set_niden - This function sets the NIDEN bit in the
* control register with the given value.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_niden(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status | XDCFG_CTRL_NIDEN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status & (~XDCFG_CTRL_NIDEN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_niden_status - The function returns the NIDEN bit status in
* the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: Size of the buffer.
*/
static ssize_t xdevcfg_show_niden_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 niden_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
niden_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_NIDEN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (niden_status >> 4));
return status;
}
static DEVICE_ATTR(enable_dbg_nonin, 0644, xdevcfg_show_niden_status,
xdevcfg_set_niden);
/**
* xdevcfg_set_spiden - This function sets the SPIDEN bit in the
* control register with the given value.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_spiden(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status | XDCFG_CTRL_SPIDEN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status & (~XDCFG_CTRL_SPIDEN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_spiden_status - The function returns the SPIDEN bit status in
* the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: Size of the buffer.
*/
static ssize_t xdevcfg_show_spiden_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 spiden_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
spiden_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SPIDEN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (spiden_status >> 5));
return status;
}
static DEVICE_ATTR(enable_sec_dbg_in, 0644, xdevcfg_show_spiden_status,
xdevcfg_set_spiden);
/**
* xdevcfg_set_spniden - This function sets the SPNIDEN bit in the
* control register with the given value.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or the size of buffer
*/
static ssize_t xdevcfg_set_spniden(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status | XDCFG_CTRL_SPNIDEN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status & (~XDCFG_CTRL_SPNIDEN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_spniden_status - The function returns the SPNIDEN bit status
* in the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: Size of the buffer.
*/
static ssize_t xdevcfg_show_spniden_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 spniden_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
spniden_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SPNIDEN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (spniden_status >> 6));
return status;
}
static DEVICE_ATTR(enable_sec_dbg_nonin, 0644, xdevcfg_show_spniden_status,
xdevcfg_set_spniden);
/**
* xdevcfg_set_seu - This function sets the SEU_EN bit in the
* control register with the given value
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_seu(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status | XDCFG_CTRL_SEU_EN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status & (~XDCFG_CTRL_SEU_EN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_seu_status - The function returns the SEU_EN bit status
* in the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_seu_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 seu_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
seu_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SEU_EN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (seu_status > 8));
return status;
}
static DEVICE_ATTR(enable_seu, 0644, xdevcfg_show_seu_status, xdevcfg_set_seu);
/**
* xdevcfg_set_aes - This function sets the AES_EN bits in the
* control register with either all 1s or all 0s.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*
* The user must send only one bit in the buffer to notify whether he wants to
* either set or reset these bits.
*/
static ssize_t xdevcfg_set_aes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 ctrl_reg_status;
unsigned long flags;
unsigned long mask_bit;
int status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
ctrl_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status < 0)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status |
XDCFG_CTRL_PCFG_AES_EN_MASK |
XDCFG_CTRL_PCAP_RATE_EN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET,
(ctrl_reg_status &
~(XDCFG_CTRL_PCFG_AES_EN_MASK |
XDCFG_CTRL_PCAP_RATE_EN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_aes_status - The function returns the AES_EN bit status
* in the control register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_aes_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 aes_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
aes_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_CTRL_OFFSET) & XDCFG_CTRL_PCFG_AES_EN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (aes_status >> 9));
return status;
}
static DEVICE_ATTR(enable_aes, 0644, xdevcfg_show_aes_status, xdevcfg_set_aes);
/**
* xdevcfg_set_aes_en_lock - This function sets the LOCK_AES_EN bit in the
* lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_aes_en_lock(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 aes_en_lock_status;
unsigned long flags;
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
aes_en_lock_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(aes_en_lock_status | XDCFG_LOCK_AES_EN_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(aes_en_lock_status &
(~XDCFG_LOCK_AES_EN_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_aes_en_lock_status - The function returns the LOCK_AES_EN bit
* status in the lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_aes_en_lock_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 aes_en_lock_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
aes_en_lock_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET) & XDCFG_LOCK_AES_EN_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (aes_en_lock_status >> 3));
return status;
}
static DEVICE_ATTR(aes_en_lock, 0644, xdevcfg_show_aes_en_lock_status,
xdevcfg_set_aes_en_lock);
/**
* xdevcfg_set_seu_lock - This function sets the LOCK_SEU bit in the
* lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_seu_lock(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 seu_lock_status;
unsigned long flags;
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
seu_lock_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(seu_lock_status | XDCFG_LOCK_SEU_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(seu_lock_status & (~XDCFG_LOCK_SEU_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_seu_lock_status - The function returns the LOCK_SEU bit
* status in the lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_seu_lock_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 seu_lock_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
seu_lock_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET) & XDCFG_LOCK_SEU_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (seu_lock_status >> 2));
return status;
}
static DEVICE_ATTR(seu_lock, 0644, xdevcfg_show_seu_lock_status,
xdevcfg_set_seu_lock);
/**
* xdevcfg_set_dbg_lock - This function sets the LOCK_DBG bit in the
* lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_dbg_lock(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u32 lock_reg_status;
unsigned long flags;
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
lock_reg_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
goto err_clk;
if (mask_bit > 1) {
status = -EINVAL;
goto err_clk;
}
spin_lock_irqsave(&drvdata->lock, flags);
if (mask_bit)
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(lock_reg_status | XDCFG_LOCK_DBG_MASK));
else
xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET,
(lock_reg_status & (~XDCFG_LOCK_DBG_MASK)));
spin_unlock_irqrestore(&drvdata->lock, flags);
clk_disable(drvdata->clk);
return size;
err_clk:
clk_disable(drvdata->clk);
return status;
}
/**
* xdevcfg_show_dbg_lock_status - The function returns the LOCK_DBG bit
* status in the lock register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_dbg_lock_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 dbg_lock_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
dbg_lock_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_LOCK_OFFSET) & XDCFG_LOCK_DBG_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", dbg_lock_status);
return status;
}
static DEVICE_ATTR(dbg_lock, 0644, xdevcfg_show_dbg_lock_status,
xdevcfg_set_dbg_lock);
/**
* xdevcfg_show_prog_done_status - The function returns the PROG_DONE bit
* status in the interrupt status register.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_prog_done_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 prog_done_status;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = clk_enable(drvdata->clk);
if (status)
return status;
prog_done_status = xdevcfg_readreg(drvdata->base_address +
XDCFG_INT_STS_OFFSET) & XDCFG_IXR_PCFG_DONE_MASK;
clk_disable(drvdata->clk);
status = sprintf(buf, "%d\n", (prog_done_status >> 2));
return status;
}
static DEVICE_ATTR(prog_done, 0644, xdevcfg_show_prog_done_status,
NULL);
/**
* xdevcfg_set_is_partial_bitstream - This function sets the
* is_partial_bitstream variable. If is_partial_bitstream is set,
* then PROG_B is not asserted (xdevcfg_reset_pl) and also
* zynq_slcr_init_preload_fpga and zynq_slcr_init_postload_fpga functions
* are not invoked.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* @size: The number of bytes used from the buffer
* returns: -EINVAL if invalid parameter is sent or size
*/
static ssize_t xdevcfg_set_is_partial_bitstream(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
unsigned long mask_bit;
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = kstrtoul(buf, 10, &mask_bit);
if (status)
return status;
if (mask_bit > 1)
return -EINVAL;
if (mask_bit)
drvdata->is_partial_bitstream = 1;
else
drvdata->is_partial_bitstream = 0;
return size;
}
/**
* xdevcfg_show_is_partial_bitstream_status - The function returns the
* value of is_partial_bitstream variable.
* @dev: Pointer to the device structure.
* @attr: Pointer to the device attribute structure.
* @buf: Pointer to the buffer location for the configuration
* data.
* returns: size of the buffer.
*/
static ssize_t xdevcfg_show_is_partial_bitstream_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t status;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
status = sprintf(buf, "%d\n", drvdata->is_partial_bitstream);
return status;
}
static DEVICE_ATTR(is_partial_bitstream, 0644,
xdevcfg_show_is_partial_bitstream_status,
xdevcfg_set_is_partial_bitstream);
static const struct attribute *xdevcfg_attrs[] = {
&dev_attr_prog_done.attr, /* PCFG_DONE bit in Intr Status register */
&dev_attr_dbg_lock.attr, /* Debug lock bit in Lock register */
&dev_attr_seu_lock.attr, /* SEU lock bit in Lock register */
&dev_attr_aes_en_lock.attr, /* AES EN lock bit in Lock register */
&dev_attr_enable_aes.attr, /* AES EN bit in Control register */
&dev_attr_enable_seu.attr, /* SEU EN bit in Control register */
&dev_attr_enable_sec_dbg_nonin.attr, /*SPNIDEN bit in Control register*/
&dev_attr_enable_sec_dbg_in.attr, /*SPIDEN bit in Control register */
&dev_attr_enable_dbg_nonin.attr, /* NIDEN bit in Control register */
&dev_attr_enable_dbg_in.attr, /* DBGEN bit in Control register */
&dev_attr_enable_dap.attr, /* DAP_EN bits in Control register */
&dev_attr_is_partial_bitstream.attr, /* Flag for partial bitstream */
NULL,
};
static const struct attribute_group xdevcfg_attr_group = {
.attrs = (struct attribute **) xdevcfg_attrs,
};
static ssize_t fclk_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fclk_data *pdata = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->enabled);
}
static ssize_t fclk_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long enable;
int ret;
struct fclk_data *pdata = dev_get_drvdata(dev);
ret = kstrtoul(buf, 0, &enable);
if (ret)
return -EINVAL;
enable = !!enable;
if (enable == pdata->enabled)
return count;
if (enable)
ret = clk_enable(pdata->clk);
else
clk_disable(pdata->clk);
if (ret)
return ret;
pdata->enabled = enable;
return count;
}
static DEVICE_ATTR(enable, 0644, fclk_enable_show, fclk_enable_store);
static ssize_t fclk_set_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fclk_data *pdata = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(pdata->clk));
}
static ssize_t fclk_set_rate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
unsigned long rate;
struct fclk_data *pdata = dev_get_drvdata(dev);
ret = kstrtoul(buf, 0, &rate);
if (ret)
return -EINVAL;
rate = clk_round_rate(pdata->clk, rate);
ret = clk_set_rate(pdata->clk, rate);
return ret ? ret : count;
}
static DEVICE_ATTR(set_rate, 0644, fclk_set_rate_show, fclk_set_rate_store);
static ssize_t fclk_round_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fclk_data *pdata = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%lu => %lu\n", pdata->rate_rnd,
clk_round_rate(pdata->clk, pdata->rate_rnd));
}
static ssize_t fclk_round_rate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
unsigned long rate;
struct fclk_data *pdata = dev_get_drvdata(dev);
ret = kstrtoul(buf, 0, &rate);
if (ret)
return -EINVAL;
pdata->rate_rnd = rate;
return count;
}
static DEVICE_ATTR(round_rate, 0644, fclk_round_rate_show,
fclk_round_rate_store);
static const struct attribute *fclk_ctrl_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_set_rate.attr,
&dev_attr_round_rate.attr,
NULL,
};
static const struct attribute_group fclk_ctrl_attr_grp = {
.attrs = (struct attribute **)fclk_ctrl_attrs,
};
static ssize_t xdevcfg_fclk_export_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i, ret;
struct device *subdev;
struct fclk_data *fdata;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
if (!strncmp(buf, fclk_name[i], strlen(fclk_name[i])))
break;
}
if (i < NUMFCLKS && !drvdata->fclk_exported[i]) {
drvdata->fclk_exported[i] = 1;
subdev = device_create(drvdata->fclk_class, dev, MKDEV(0, 0),
NULL, fclk_name[i]);
if (IS_ERR(subdev))
return PTR_ERR(subdev);
ret = clk_prepare(drvdata->fclk[i]);
if (ret)
return ret;
fdata = kzalloc(sizeof(*fdata), GFP_KERNEL);
if (!fdata) {
ret = -ENOMEM;
goto err_unprepare;
}
fdata->clk = drvdata->fclk[i];
dev_set_drvdata(subdev, fdata);
ret = sysfs_create_group(&subdev->kobj, &fclk_ctrl_attr_grp);
if (ret)
goto err_free;
} else {
return -EINVAL;
}
return size;
err_free:
kfree(fdata);
err_unprepare:
clk_unprepare(drvdata->fclk[i]);
return ret;
}
static ssize_t xdevcfg_fclk_export_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
ssize_t count = 0;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
if (!drvdata->fclk_exported[i])
count += scnprintf(buf + count, PAGE_SIZE - count,
"%s\n", fclk_name[i]);
}
return count;
}
static DEVICE_ATTR(fclk_export, 0644, xdevcfg_fclk_export_show,
xdevcfg_fclk_export_store);
static int match_fclk(struct device *dev, const void *data)
{
struct fclk_data *fdata = dev_get_drvdata(dev);
return fdata->clk == data;
}
static ssize_t xdevcfg_fclk_unexport_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
if (!strncmp(buf, fclk_name[i], strlen(fclk_name[i])))
break;
}
if (i < NUMFCLKS && drvdata->fclk_exported[i]) {
struct fclk_data *fdata;
struct device *subdev;
drvdata->fclk_exported[i] = 0;
subdev = class_find_device(drvdata->fclk_class, NULL,
drvdata->fclk[i], match_fclk);
fdata = dev_get_drvdata(subdev);
if (fdata->enabled)
clk_disable(fdata->clk);
clk_unprepare(fdata->clk);
kfree(fdata);
device_unregister(subdev);
put_device(subdev);
} else {
return -EINVAL;
}
return size;
}
static ssize_t xdevcfg_fclk_unexport_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
ssize_t count = 0;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
if (drvdata->fclk_exported[i])
count += scnprintf(buf + count, PAGE_SIZE - count,
"%s\n", fclk_name[i]);
}
return count;
}
static DEVICE_ATTR(fclk_unexport, 0644, xdevcfg_fclk_unexport_show,
xdevcfg_fclk_unexport_store);
static const struct attribute *fclk_exp_attrs[] = {
&dev_attr_fclk_export.attr,
&dev_attr_fclk_unexport.attr,
NULL,
};
static const struct attribute_group fclk_exp_attr_grp = {
.attrs = (struct attribute **)fclk_exp_attrs,
};
static void xdevcfg_fclk_init(struct device *dev)
{
int i;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
drvdata->fclk[i] = clk_get(dev, fclk_name[i]);
if (IS_ERR(drvdata->fclk[i])) {
dev_warn(dev, "fclk not found\n");
return;
}
}
drvdata->fclk_class = class_create(THIS_MODULE, "fclk");
if (IS_ERR(drvdata->fclk_class)) {
dev_warn(dev, "failed to create fclk class\n");
return;
}
if (sysfs_create_group(&dev->kobj, &fclk_exp_attr_grp))
dev_warn(dev, "failed to create sysfs entries\n");
}
static void xdevcfg_fclk_remove(struct device *dev)
{
int i;
struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev);
for (i = 0; i < NUMFCLKS; i++) {
if (drvdata->fclk_exported[i]) {
struct fclk_data *fdata;
struct device *subdev;
drvdata->fclk_exported[i] = 0;
subdev = class_find_device(drvdata->fclk_class, NULL,
drvdata->fclk[i], match_fclk);
fdata = dev_get_drvdata(subdev);
if (fdata->enabled)
clk_disable(fdata->clk);
clk_unprepare(fdata->clk);
kfree(fdata);
device_unregister(subdev);
put_device(subdev);
}
}
class_destroy(drvdata->fclk_class);
sysfs_remove_group(&dev->kobj, &fclk_exp_attr_grp);
}
/**
* xdevcfg_drv_probe - Probe call for the device.
*
* @pdev: handle to the platform device structure.
*
* Returns: 0 on success, negative error otherwise.
*
* It does all the memory allocation and registration for the device.
*/
static int xdevcfg_drv_probe(struct platform_device *pdev)
{
struct resource *res;
struct xdevcfg_drvdata *drvdata;
dev_t devt;
int retval;
u32 ctrlreg;
struct device_node *np;
const void *prop;
int size;
struct device *dev;
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drvdata->base_address = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drvdata->base_address))
return PTR_ERR(drvdata->base_address);
drvdata->irq = platform_get_irq(pdev, 0);
retval = devm_request_irq(&pdev->dev, drvdata->irq, &xdevcfg_irq,
0, dev_name(&pdev->dev), drvdata);
if (retval) {
dev_err(&pdev->dev, "No IRQ available");
return retval;
}
platform_set_drvdata(pdev, drvdata);
spin_lock_init(&drvdata->lock);
mutex_init(&drvdata->sem);
drvdata->is_open = 0;
drvdata->is_partial_bitstream = 0;
drvdata->dma_done = 0;
drvdata->error_status = 0;
dev_info(&pdev->dev, "ioremap %pa to %p\n",
&res->start, drvdata->base_address);
drvdata->clk = devm_clk_get(&pdev->dev, "ref_clk");
if (IS_ERR(drvdata->clk)) {
dev_err(&pdev->dev, "input clock not found\n");
return PTR_ERR(drvdata->clk);
}
retval = clk_prepare_enable(drvdata->clk);
if (retval) {
dev_err(&pdev->dev, "unable to enable clock\n");
return retval;
}
/*
* Figure out from the device tree if this is running on the EP107
* emulation platform as it doesn't match the silicon exactly and the
* driver needs to work accordingly.
*/
np = of_get_next_parent(pdev->dev.of_node);
np = of_get_next_parent(np);
prop = of_get_property(np, "compatible", &size);
if (prop != NULL) {
if ((strcmp((const char *)prop, "xlnx,zynq-ep107")) == 0)
drvdata->ep107 = 1;
else
drvdata->ep107 = 0;
}
/* Unlock the device */
xdevcfg_writereg(drvdata->base_address + XDCFG_UNLOCK_OFFSET,
0x757BDF0D);
/*
* Set the configuration register with the following options
* - Reset FPGA
* - Enable the PCAP interface
* - Set the throughput rate for maximum speed
* - Set the CPU in user mode
*/
ctrlreg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET);
ctrlreg &= ~XDCFG_CTRL_PCAP_PR_MASK;
ctrlreg |= XDCFG_CTRL_PCFG_PROG_B_MASK | XDCFG_CTRL_PCAP_MODE_MASK;
xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, ctrlreg);
/* Ensure internal PCAP loopback is disabled */
ctrlreg = xdevcfg_readreg(drvdata->base_address + XDCFG_MCTRL_OFFSET);
xdevcfg_writereg(drvdata->base_address + XDCFG_MCTRL_OFFSET,
(~XDCFG_MCTRL_PCAP_LPBK_MASK &
ctrlreg));
retval = alloc_chrdev_region(&devt, 0, XDEVCFG_DEVICES, DRIVER_NAME);
if (retval < 0)
goto failed5;
drvdata->devt = devt;
cdev_init(&drvdata->cdev, &xdevcfg_fops);
drvdata->cdev.owner = THIS_MODULE;
retval = cdev_add(&drvdata->cdev, devt, 1);
if (retval) {
dev_err(&pdev->dev, "cdev_add() failed\n");
goto failed6;
}
drvdata->class = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(drvdata->class)) {
dev_err(&pdev->dev, "failed to create class\n");
goto failed6;
}
dev = device_create(drvdata->class, &pdev->dev, devt, drvdata,
DRIVER_NAME);
if (IS_ERR(dev)) {
dev_err(&pdev->dev, "unable to create device\n");
goto failed7;
}
/* create sysfs files for the device */
retval = sysfs_create_group(&(pdev->dev.kobj), &xdevcfg_attr_group);
if (retval) {
dev_err(&pdev->dev, "Failed to create sysfs attr group\n");
cdev_del(&drvdata->cdev);
goto failed8;
}
xdevcfg_fclk_init(&pdev->dev);
clk_disable(drvdata->clk);
return 0; /* Success */
failed8:
device_destroy(drvdata->class, drvdata->devt);
failed7:
class_destroy(drvdata->class);
failed6:
/* Unregister char driver */
unregister_chrdev_region(devt, XDEVCFG_DEVICES);
failed5:
clk_disable_unprepare(drvdata->clk);
return retval;
}
/**
* xdevcfg_drv_remove - Remove call for the device.
*
* @pdev: handle to the platform device structure.
*
* Returns: 0 or error status.
*
* Unregister the device after releasing the resources.
*/
static int xdevcfg_drv_remove(struct platform_device *pdev)
{
struct xdevcfg_drvdata *drvdata;
drvdata = platform_get_drvdata(pdev);
if (!drvdata)
return -ENODEV;
unregister_chrdev_region(drvdata->devt, XDEVCFG_DEVICES);
sysfs_remove_group(&pdev->dev.kobj, &xdevcfg_attr_group);
xdevcfg_fclk_remove(&pdev->dev);
device_destroy(drvdata->class, drvdata->devt);
class_destroy(drvdata->class);
cdev_del(&drvdata->cdev);
clk_unprepare(drvdata->clk);
return 0; /* Success */
}
static const struct of_device_id xdevcfg_of_match[] = {
{ .compatible = "xlnx,zynq-devcfg-1.0", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, xdevcfg_of_match);
/* Driver Structure */
static struct platform_driver xdevcfg_platform_driver = {
.probe = xdevcfg_drv_probe,
.remove = xdevcfg_drv_remove,
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = xdevcfg_of_match,
},
};
module_platform_driver(xdevcfg_platform_driver);
MODULE_AUTHOR("Xilinx, Inc");
MODULE_DESCRIPTION("Xilinx Device Config Driver");
MODULE_LICENSE("GPL");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment