They are untested for really long time. Remove them.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
+++ /dev/null
-Documentation for pmods, a set of peripheral modules provided by Digilent Inc.,
-which can be plugged to various development boards to add additional functionalities.
-These drivers are maintained by Digilent Inc.
-
-00-INDEX
- - this file
-pmodoled.txt
- - PmodOLED: 128 by 32 pixel 0.9" Organic LED Graphic Display
+++ /dev/null
-PmodAD1
-========
-
-Copyright 2012, Digilent Inc.
-
-
-Description
------------
-
-The Analog to Digital Module Converter Board converts signals at a
-maximum sampling rate of one million samples per second.
-
-The Digilent PmodAD1 relies on two Analog Devices AD7476, thus implementing two
-simultaneous A/D conversion channels, each with an 12-bit converter.
-
-The AD1 converts an analog input signal ranging from 0-3.3 volts to a 12-bit
-digital value in the range 0 to 4095 (0x0FFF).
-
-This Linux driver is based on SPI. Because SPI can not read on Data Out line of
-SPI, only the channel corresponding to the second AD7476 can be accessed using
-this driver (the channel corresponding to P3, P4 pins of J2 connector of PmodAD1).
-
-The driver is implemented as a character driver. The basic action of the driver is
-read, when 12 bits data is read over SPI.
-
-The Reference Manual for PmodAD1 display is available online at
-Digilent Inc. Website (www.digilentinc.com).
-
-
-Interface
----------
-
-Signal Description
-
-SDOUT SPI Data In (MISO)
-SCLK SPI Clock
-SS Slave Select
-
-
-Devicetree
-----------
-
-Required Properties:
-- compatible : Should be "dlgnt,pmodad1"
-- spi-bus-num : Should specify the bus number for PmodAD1 SPI controller.
- This value cannot be shared by any other SPI controller present in the
- device tree.
-- spi-sclk-gpio : Should specify the GPIO for SCLK, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- spi-sdout-gpio : Should specify the GPIO for SDOUT, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Optional Properties:
-- spi-cs-gpio : Should specify the GPIO for CS, see "gpios property" in
- Documentation/devicetree/gpio.txt. If unspecified, CS is assumed to be
- tied to ground.
-- spi-speed-hz : Should specify the spi speed (in Hz). If unspecified, spi
- speed is assumed to be 625000 (625 kHz).
-
-Example:
-
- pmodad1 {
- compatible = "dglnt,pmodad1";
- spi-bus-num = <0x5>;
- spi-speed-hz = <1000000>;
- spi-sclk-gpio = <0x8 85 0>;
- spi-sdout-gpio = <0x8 84 0>;
- spi-cs-gpio = <0x8 82 0>;
- };
-
-This example corresponds to PmodAD1 plugged into JA connector, pins 1-7.
-
-Configuration
--------------
-
-The PmodAD1 is located in the kernel configuration menu at
-Device Drivers -> Pmods -> PmodAD1. The driver can be built into the kernel
-by selecting (*) for it, or loadable module by selecting (M) for it.
-
-
-Device Nodes
-------------
-
-A char device node will be created for each PmodAD1 device automatically.
-The name of the node is default to the one declared in the device tree.
-
-Reading from the device
------------
-The PmodAD1 is a "read only" device, read being its main action.
-2 bytes of data are read over the spi, a mask is applied so that the 12 LSB
-bits are used to fill the read buffer.
-
-Example of commands
------------
-
-- Read (repeatedly) the device
-hexdump -v -d /dev/pmodad1
+++ /dev/null
-PmodCLP
-========
-
-Copyright 2012, Digilent Inc.
-
-
-Description
------------
-
-The PmodCLP is a 16x2 character LCD module that uses two Pmod connectors to present a
-8-bit parallel data interface to system boards.
-The PmodCLP features an SPI-controlled monochrome LCD 16x2 Character Display,
-perfect for embedded applications requiring small, simple text output.
-
-The PmodCLP uses simple terminal-like display interface. It provides
-flexible communications using UART, SPI or TWI interface.
-
-Some of the PmodCLP implement a backlight feature. This is implemented by using the
-optional bk-gpio in the Device Tree.
-
-This Linux character driver sends commands and data to the device using parallel access
-to GPIOs. The set of commands is based on escape characters.
-
-The Reference Manual for PmodCLP device is available online at
-Digilent Inc. Website (www.digilentinc.com).
-
-
-Interface
----------
-
-Signal Description
-
-RS RS (Register Select) signal of the parallel interface
-RW RW (Read/Write) signal of the parallel interface
-E E (Enable) signal of the parallel interface
-BK (Optional) - BackLight signal
-DATA0 Signal for Bit 0 of the parallel interface
-DATA1 Signal for Bit 1 of the parallel interface
-DATA2 Signal for Bit 2 of the parallel interface
-DATA3 Signal for Bit 3 of the parallel interface
-DATA4 Signal for Bit 4 of the parallel interface
-DATA5 Signal for Bit 5 of the parallel interface
-DATA6 Signal for Bit 6 of the parallel interface
-DATA7 Signal for Bit 7 of the parallel interface
-
-Devicetree
-----------
-
-Required Properties:
-- compatible : Should be "dlgnt,pmodclp"
-- rs-gpio : Should specify the GPIO for RS, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- rw-gpio : Should specify the GPIO for RW, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- e-gpio : Should specify the GPIO for E, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data0-gpio : Should specify the GPIO for DATA0, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data1-gpio : Should specify the GPIO for DATA1, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data2-gpio : Should specify the GPIO for DATA2, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data3-gpio : Should specify the GPIO for DATA3, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data4-gpio : Should specify the GPIO for DATA4, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data5-gpio : Should specify the GPIO for DATA5, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data6-gpio : Should specify the GPIO for DATA6, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- data7-gpio : Should specify the GPIO for DATA7, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Optional Properties:
-- bk-gpio : Should specify the GPIO for BK, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Example:
-(corresponds to
-J1 connector of PmodCLP connected to JA connector of Zed
-J2 connector of PmodCLP connected to Jb connector of Zed):
-
- pmodclp {
- compatible = "dglnt,pmodclp";
- rs-gpio = <0x8 94 0>;
- rw-gpio = <0x8 95 0>;
- e-gpio = <0x8 96 0>;
- bk-gpio = <0x8 97 0>;
- data0-gpio = <0x8 82 0>;
- data1-gpio = <0x8 83 0>;
- data2-gpio = <0x8 84 0>;
- data3-gpio = <0x8 85 0>;
- data4-gpio = <0x8 86 0>;
- data5-gpio = <0x8 87 0>;
- data6-gpio = <0x8 88 0>;
- data7-gpio = <0x8 89 0>;
- };
-
-
-Configuration
--------------
-
-The PmodCLP is located in the kernel configuration menu at
-Device Drivers -> Pmods -> PmodCLP. The driver can be built into the kernel
-by selecting (*) for it, or loadable module by selecting (M) for it.
-
-
-Device Nodes
-------------
-
-A char device node will be created for each PmodCLP device automatically.
-The name of the node is default to the one declared in the device tree.
-
-
-Instruction set
------------
-Instructions are sent using escape sequences. An escape sequence
-begins with ESC (character code 0x1B) followed by the left
-square bracket ‘[‘ , followed by 0 or more parameters separated by
-semicolons ‘;’ and ending with the command character.
-
-The implemented commands are:
-
-<pr>;<pc>H - set cursor position to <pr> row and <pc> col
-
-<pn>@ - scroll left <pn> columns
-
-<pn>A - scroll right <pn> columns
-
-<ps>e - enable/disable display
- 0 = display off,
- backlight off
- 1 = display on,
- backlight off
- 2 = display off,
- backlight on
- 3 = display on,
- backlight on
-
-<ps>c - set cursor mode
- 0 = cursor off
- 1 = cursor on,
- blink off
- 2 = cursor on,
- blink on
-
-j - clear display and home cursor
-
-
-<pn>;…<pn>;<ps>d - define user programmable character
-Be aware that after defining a user character you must issue a
-set position command.
-
-Notations:
-<pr> row number (0 - 1)
-<pc> column number (0 – 39)
-<pn> numeric parameter
- - decimal: 122 for ex.
- - hex: 0x7A for ex.
- - binary: 0b01111010 for ex.
-<ps> decimal selection parameter
-
-
-Examples of commands
------------
-
-- Set display on, backlight on
-echo -n -e "\x1B[3e" > /dev/pmodclp
-
-- Clear screen, cursor home
-echo -n -e "\x1B[j" > /dev/pmodclp
-
-- Position cursor (row 1, col 0)
-echo -n -e "\x1B[1;0H" > /dev/pmodclp
-
-- Display string
-echo -n -e "Digilent" > /dev/pmodclp
-
-- Display string on 2 rows (when current row is the first)
-echo -n -e "Hello\nDigilent" > /dev/pmodclp
-
-- Scroll right 2 positions
-echo -n -e "\x1B[2A" > /dev/pmodclp
-
-- Scroll left 2 positions
-echo -n -e "\x1B[2@" > /dev/pmodclp
-
-
-- Define user char 1
-echo -n -e "\x1B[0x00;0x0A;0x15;0x11;0x0A;0x04;0x00;0x00;1d" > /dev/pmodclp
-
-- Display user char 1 on row 1, col 3
-echo -n -e "\x1B[1;3H" > /dev/pmodclp
-echo -n -e "\x01" > /dev/pmodclp
-
-
-
-
+++ /dev/null
-PmodCLS
-========
-
-Copyright 2012, Digilent Inc.
-
-
-Description
------------
-
-The PmodCLS features an SPI-controlled monochrome LCD 16x2 Character Display,
-perfect for embedded applications requiring small, simple text output.
-
-The PmodCLS uses simple terminal-like display interface. It provides
-flexible communications using UART, SPI or TWI interface.
-
-This Linux character driver uses an SPI interface in order to configure the device,
-as well as to send the text to be displayed on the device.
-
-Commands are sent and characters are written to the display simply by sending
-characters over the communication link.
-
-
-The Reference Manual for PmodCLS display is available online at
-Digilent Inc. Website (www.digilentinc.com).
-
-
-Interface
----------
-
-Signal Description
-
-SDIN SPI Data In (MOSI)
-SCLK SPI Clock
-SS Slave Select
-
-
-Devicetree
-----------
-
-Required Properties:
-- compatible : Should be "dlgnt,pmodcls"
-- spi-bus-num : Should specify the bus number for PmodCLS SPI controller.
- This value cannot be shared by any other SPI controller present in the
- device tree.
-- spi-sclk-gpio : Should specify the GPIO for SCLK, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- spi-sdin-gpio : Should specify the GPIO for SDIN, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Optional Properties:
-- spi-cs-gpio : Should specify the GPIO for CS, see "gpios property" in
- Documentation/devicetree/gpio.txt. If unspecified, CS is assumed to be
- tied to ground.
-- spi-speed-hz : Should specify the spi speed (in Hz). If unspecified, spi
- speed is assumed to be 625000 (625 kHz).
-
-Examples:
-
- pmodcls {
- compatible = "dglnt,pmodcls";
- spi-bus-num = <0x3>;
- spi-speed-hz = <625000>;
- spi-sclk-gpio = <0x8 85 0>;
- spi-sdin-gpio = <0x8 83 0>;
- spi-cs-gpio = <0x8 82 0>;
- };
-
-
-Configuration
--------------
-
-The PmodCLS is located in the kernel configuration menu at
-Device Drivers -> Pmods -> PmodCLS. The driver can be built into the kernel
-by selecting (*) for it, or as a loadable module by selecting (M) for it.
-
-
-Device Nodes
-------------
-
-A char device node will be created for each PmodCLS device automatically.
-The name of the node is default to the one declared in the device tree.
-
-Instruction set
------------
-Instructions are sent using escape sequences. An escape sequence
-begins with ESC (character code 0x1B) followed by the left
-square bracket ‘[‘ , followed by 0 or more parameters separated by
-semicolons ‘;’ and ending with the command character.
-
-The commands are (as stated in the PmodCLS reference Manual):
-
-<pr>;<pc>H - set cursor position to <pr> row and <pc> col
-s - save cursor position
-u - restore saved cursor position
-j - clear display and home cursor
-<ps>K - erase within line
- 0 = current position to end of line
- 1 = start of line to current position
- 2 = entire line
-<ps>N erase field in current line
- <ps> = number of chars starting at current position
-<pn>@ - scroll left <pn> columns
-<pn>A - scroll right <pn> columns
-* - reset; equivalent to cycling power of PmodCLS
-<ps>e - enable/disable display
- 0 = display off,
- backlight off
- 1 = display on,
- backlight off
- 2 = display off,
- backlight on
- 3 = display on,
- backlight on
-<ps>h - set display mode
- 0 = wrap line at 16 characters
- 1 = wrap line at 40 characters
-<ps>c - set cursor mode
- 0 = cursor off
- 1 = cursor on,
- blink off
- 2 = cursor on,
- blink on
-<pn>a - save TWI address in EEPROM to <pn>
-<pn>b - save baud rate value in EEPROM to <pn>
-<pt>p - program character table into LCD
-<pt>t - save RAM character table to EEPROM
-<pt>l - load EEPROM character table to RAM
-<pn>;…<pn>;<ps>d- define user programmable character
- Be aware that after defining a user character you must
- program the character table into LCD (command 'p').
-<ps>m - save communication mode to EEPROM
-w - enable write to EEPROM
-<ps>n - save cursor mode to EEPROM
-<ps>o - save display mode to EEPROM
-
-Notations:
-<pr> row number (0 - 1)
-<pc> column number (0 – 39)
-<pn> numeric parameter
- - decimal: 122 for ex.
- - hex: 0x7A for ex.
- - binary: 0b01111010 for ex.
-<ps> decimal selection parameter
-<pt> character table selector
- (0 – 2 in EEPROM, 3 in RAM)
-
-
-Example of commands
------------
-
-- Set backlight on, display on
-echo -n -e "\x1B[3e" > /dev/pmodcls
-
-- Clear screen, cursor home
-echo -n -e "\x1B[j" > /dev/pmodcls
-
-- Position cursor (row 1, col 0)
-echo -n -e "\x1B[1;0H" > /dev/pmodcls
-
-- Display string
-echo -n -e "Digilent" > /dev/pmodcls
-
-- Display string on 2 rows (when current row is the first)
-echo -n -e "Hello\nDigilent" > /dev/pmodcls
-
-- Define user character 1 (in RAM)
-echo -n -e "\x1B[0;10;21;17;10;4;0;0;1d\x1B[3p" > /dev/pmodcls
-
-- Display user char 1
-echo -n -e "\x01" > /dev/pmodcls
-
-
-
-
+++ /dev/null
-PmodDA1
-========
-
-Copyright 2012, Digilent Inc.
-
-
-Description
------------
-
-The Digilent PmodDA1 Digital To Analog Module Converter Board (the DA1) converts
-signals from digital to analog at up to one MSa per second.
-
-The Digilent PmodDA1 relies on two Analog Devices AD7303, thus implementing four
-simultaneous D/A conversion channels, each with an 8-bit converter that can
-process a separate digital signal.
-
-The PmodDA1 converts an 8 bit digital value to an analog output
-ranging from 0-3.3 volts.
-
-This Linux driver is based on SPI. Because SPI can not write on Data Out line of
-SPI, only the 2 channels corresponding to the first AD7303 (the channels corresponding
-to A1 and B1 pins of J2 connector of PmodDA1) can be accessed using this driver.
-
-The driver is implemented as a character driver. Corresponding to each channel,
-two separates nodes are created by the driver code, having different minor numbers.
-
-The basic action of the driver is write, when 8 bits data is outputted to each of the
-two channels. Still, read function is also implemented, by providing the last written
-value.
-
-The Reference Manual for PmodDA1 device is available online at
-Digilent Inc. Website (www.digilentinc.com).
-
-
-Interface
----------
-
-Signal Description
-
-SDIN SPI Data Out (MOSI)
-SCLK SPI Clock
-SS Slave Select
-
-
-Devicetree
-----------
-
-Required Properties:
-- compatible : Should be "dlgnt,pmodda1"
-- spi-bus-num : Should specify the bus number for PmodDA1 SPI controller.
- This value cannot be shared by any other SPI controller present in the
- device tree.
-- spi-sclk-gpio : Should specify the GPIO for SCLK, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- spi-sdin-gpio : Should specify the GPIO for SDIN, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Optional Properties:
-- spi-cs-gpio : Should specify the GPIO for CS, see "gpios property" in
- Documentation/devicetree/gpio.txt. If unspecified, CS is assumed to be
- tied to ground.
-- spi-speed-hz : Should specify the spi speed (in Hz). If unspecified, spi
- speed is assumed to be 625000 (625 kHz).
-
-Examples:
-
- pmodda1 {
- compatible = "dglnt,pmodda1";
- spi-bus-num = <0x4>;
- spi-speed-hz = <625000>;
- spi-sclk-gpio = <0x8 85 0>;
- spi-sdin-gpio = <0x8 83 0>;
- spi-cs-gpio = <0x8 82 0>;
- };
-
-
-Configuration
--------------
-
-The PmodDA1 is located in the kernel configuration menu at
-Device Drivers -> Pmods -> PmodDA1. The driver can be built into the kernel
-by selecting (*) for it, or loadable module by selecting (M) for it.
-
-
-Device Nodes
-------------
-
-Two char device nodes are created for each PmodDA1 device automatically,
-having 0 and 1 as minor numbers.
-
-The name of the nodes is by default the one declared in the device tree,
-postfixed with "_0" (for channel A1) and "_1" (for channel B1).
-
-
-Writing to the device
------------
-Characters written to the device are used as 8 bits values sent to the appropriate
-converter channel. While writing to a channel the other channel is not disabled,
-allowing it to perform its current task.
-
-
-Reading from the device
------------
-The PmodDA1 is a "write only" device. Still, the driver implements a shadow register
-that also maintains the last value written to the converter. When read occurs, this
-value is used to fill the read buffer.
-
-
-Example of commands
------------
-
-- Write 0x80 value to second channel (B1) of the device
-echo -n -e "\x80" > /dev/pmodda1_1
-
-- Read (repeatedly) first channel (A1) of the device
-hexdump -C -v /dev/pmodda1_0
-
-
-
-
+++ /dev/null
-PmodOLED
-========
-
-Copyright 2012, Digilent Inc.
-
-
-Description
------------
-
-The PmodOLED features an SPI-controlled monochrome OLED display,
-perfect for embedded applications requiring small, complex visual output.
-
-The PmodOLED uses a standard 12-pin connector to display output on
-a 128x32 pixel organic LED (OLED) panel. The graphic display panel uses
-the Solomon Systech SSD1306 display controller.
-
-An SPI interface is used to configure the display,
-as well as to send the bitmap data to the device.
-
-The PmodOLED displays the last image drawn on the screen until it is
-powered down or a new image is drawn to the display. Refreshing and
-updating is handled internally.
-
-The Reference Manual for PmodOLED display is available online at
-Digilent Inc. Website (www.digilentinc.com)
-
-For more information on the OLED display interface, see the
-UG-2832HSWEG04 datasheet available online or from Univisio.
-
-The OLED display uses a compatible command set from the SSD1306 device.
-For more information, see the SSD1306 datasheet available at
-www.solomon-systech.com.
-
-
-Interface
----------
-
-Signal Description
-
-CS SPI Chip Select (Slave Select)
-SDIN SPI Data In (MOSI)
-SCLK SPI Clock
-D/C Data/Command Control
-RES Power Reset
-VBATC VBAT Battery Voltage Control
-VDDC VDD Logic Voltage Control
-
-
-Devicetree
-----------
-
-Required Properties:
-- compatible : Should be "dlgnt,pmodoled-gpio"
-- vbat-gpio : Should specify the GPIO for VBATC, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- vdd-gpio : Should specify the GPIO for VDDC, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- res-gpio : Should specify the GPIO for RES, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- dc-gpio : Should specify the GPIO for D/C, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- spi-bus-num : Should specify the bus number for PmodOLED SPI controller.
- This value cannot be shared by any other SPI controller present in the
- device tree.
-- spi-sclk-gpio : Should specify the GPIO for SCLK, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-- spi-sdin-gpio : Should specify the GPIO for SDIN, see "gpios property" in
- Documentation/devicetree/gpio.txt.
-
-Optional Properties:
-- spi-cs-gpio : Should specify the GPIO for CS, see "gpios property" in
- Documentation/devicetree/gpio.txt. If unspecified, CS is assumed to be
- tied to ground.
-
-Examples:
-
-zed_oled {
- compatible = "dglnt,pmodoled-gpio";
- /* GPIO Pins */
- vbat-gpio = <&gpiops 55 0>;
- vdd-gpio = <&gpiops 56 0>;
- res-gpio = <&gpiops 57 0>;
- dc-gpio = <&gpiops 58 0>;
- /* SPI-GPIOs */
- spi-bus-num = <2>;
- spi-sclk-gpio = <&gpiops 59 0>;
- spi-sdin-gpio = <&gpiops 60 0>;
-};
-
-pmodoled_A {
- compatible = "dglnt,pmodoled-gpio";
- vbat-gpio = <&gpiops 88 0>;
- vdd-gpio = <&gpiops 89 0>;
- res-gpio = <&gpiops 87 0>;
- dc-gpio = <&gpiops 86 0>;
- spi-bus-num = <3>;
- spi-sclk-gpio = <&gpiops 85 0>;
- spi-sdin-gpio = <&gpiops 83 0>;
- spi-cs-gpio = <&gpiops 82 0>;
-};
-
-
-Configuration
--------------
-
-The PmodOLED is located in the kernel configuration menu at
-Device Drivers -> Pmods -> PmodOLED. The driver can be built into the kernel
-by selecting (*) for it, or loadable module by selecting (M) for it.
-
-
-Device Nodes
-------------
-
-A char device node will be created for each PmodOLED device automatically.
-The name of the node is default to the one declared in the device tree.
-
-
-Read/Writes
------------
-
-The driver provides a 512 Byte display buffer for the display of PmodOLED.
-The Whole screen is divided into four lines, each of them is 128 bits wide
-and 8 bits high, as shown in the figure below.
-
- +--------------------------...----------------------------+
- + Line 4 +
- +--------------------------...----------------------------+
- + Line 3 +
- +--------------------------...----------------------------+
- + Line 2 +
- +--------------------------...----------------------------+ MSB (bit 7)
- + Line 1 +
- +--------------------------...----------------------------+ LSB (bit 0)
-byte 127 byte 0
-
-Users can perform read and write functions to the device node to access the data
-inside the display buffer.
source "drivers/staging/apf/Kconfig"
-source "drivers/staging/pmods/Kconfig"
-
source "drivers/staging/clocking-wizard/Kconfig"
source "drivers/staging/fbtft/Kconfig"
obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_XILINX_VIDEO_IP) += video/axivdma/
obj-$(CONFIG_XILINX_APF) += apf/
-obj-$(CONFIG_PMODS) += pmods/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
+++ /dev/null
-menuconfig PMODS
- bool "Pmod Support"
- depends on HAS_IOMEM && OF
- help
- Digilent PMOD Support
-
-if PMODS
-
-config PMODS_DEBUG
- bool "Enable Debug Message"
-
-config PMODOLED
- tristate "PmodOLED1"
- depends on SPI_BITBANG
- depends on SPI_GPIO
- help
- The Digilent PmodOLED1, as well as ZED on-board OLED. Uses SPI over GPIO.
-
-config PMODCLS
- tristate "pmodcls"
- depends on SPI_BITBANG
- help
- This is the Digilent PmodCLS driver. Uses SPI over GPIO.
-
-config PMODCLP
- tristate "pmodclp"
- help
- This is the Digilent PmodCLP driver. Implements parallel access.
-
-config PMODDA1
- tristate "pmodda1"
- depends on SPI_BITBANG
- help
- This is the Digilent PmodDA1 driver. Uses SPI over GPIO.
-
-config PMODAD1
- tristate "pmodad1"
- depends on SPI_BITBANG
- help
- This is the Digilent PmodAD1 driver. Uses SPI over GPIO.
-
-endif # PMODS
+++ /dev/null
-
-ccflags-$(CONFIG_PMODS_DEBUG) += -DDEBUG
-
-obj-$(CONFIG_PMODOLED) += pmodoled-gpio.o
-obj-$(CONFIG_PMODCLS) += pmodcls.o
-obj-$(CONFIG_PMODCLP) += pmodclp.o
-obj-$(CONFIG_PMODDA1) += pmodda1.o
-obj-$(CONFIG_PMODAD1) += pmodad1.o
+++ /dev/null
-/*
- * pmodad1.c - Digilent PmodAD1 driver
- *
- * Copyright (c) 2012 Digilent. All right reserved.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "pmodad1"
-#define SPI_DRIVER_NAME "pmodad1-spi"
-
-#define DEFAULT_SPI_SPEED 625000
-#define MAX_PMODAD1_DEV_NUM 16
-#define TXT_BUF_SIZE 1024
-#define MAX_NO_ROWS 2 /* The device has 2 rows */
-
-static dev_t pmodad1_dev_id;
-static unsigned int device_num;
-static unsigned int cur_minor;
-static unsigned int spi_drv_registered;
-static struct class *pmodad1_class;
-
-/* Kernel space buffer size (in bytes) for the ad1_read function.
- * Can be entered from the command line during insmod
- */
-static int read_buf_size = 512;
-module_param(read_buf_size, int, 0);
-
-struct pmodad1_device {
- char *name;
- /* R/W Mutex Lock */
- struct mutex mutex;
-
- unsigned short *val_buf;
-
- /* Pin Assignment */
-
- unsigned long iSCLK;
- unsigned long iSDOUT;
- unsigned long iCS;
-
- /* SPI Info */
- uint32_t spi_speed;
- uint32_t spi_id;
- /* platform device structures */
- struct platform_device *pdev;
- /* Char Device */
- struct cdev cdev;
- struct spi_device *spi;
- dev_t dev_id;
-};
-
-/*
- * Driver read function
- *
- * This function uses a generic SPI read to read values from the Pmod.
- * It will only read full values, so if the length from user space is
- * not a multiple of 2, it will read up to length - 1 bytes.
- *
- * Function can possibly error out if:
- * The mutex cannot be locked
- * spi_read fails on the first read
- *
- * Otherwise, the function returns the number of successful values read,
- * each with a size of 2 bytes. So for instance, if 13 bytes are read,
- * the function will return 12, indicating 6 values were read successfully
- * from the pmod. Additionally, if copy_to_user cannot successfully
- * copy everything, the number of successfully copied full values (2 bytes)
- * will be returned.
- *
- * We use goto in this function because there are multiple exit points,
- * and it prevents us from having to call mutex_unlock() for the mutex
- * each time.
- */
-static ssize_t pmodad1_read(struct file *fp, char __user *buffer, size_t length, loff_t *offset)
-{
- int status; /* spi_read return value */
- int num_reads; /* Number of values to read from Pmod */
- int i;
- ssize_t retval; /* Function return value */
- ssize_t ret; /* copy_to_user return value */
- unsigned short buf; /* Temporary storage for each read value */
- struct pmodad1_device *dev;
-
- dev = fp->private_data;
- status = 0;
- num_reads = length / 2;
-
- if (mutex_lock_interruptible(&dev->mutex)) {
- retval = -ERESTARTSYS;
- goto lock_err;
- }
-
- if (buffer == NULL) {
- retval = -EINVAL;
- goto read_out;
- }
-
- for (i = 0; i < num_reads; i++) {
- /* Use generic SPI read */
- status = spi_read(dev->spi, &buf, 2);
- if (status)
- break;
- /* Change endianness of result, if necessary
- * The result from the Pmod hardware is big endian,
- * whereas Microblaze and other CPU architectures are
- * little endian.
- */
- dev->val_buf[i] = be16_to_cpu(buf) & 0x0FFF; /* only 12 bits matters */
- }
-
- if (i == 0) {
- dev_err(&dev->spi->dev, "SPI read failure: %d\n", status);
- retval = status;
- goto read_out;
- }
-
- /*
- * Only copy full values (2 bytes) in the case of a user space length
- * that is not a multiple of 2.
- */
- ret = copy_to_user(buffer, (void *)dev->val_buf, i * 2);
-
- retval = num_reads * 2 - (ret + (ret % 2));
-read_out:
- mutex_unlock(&dev->mutex);
-lock_err:
- return retval;
-}
-
-/**
- * A basic open function.
- */
-static int pmodad1_open(struct inode *inode, struct file *fp)
-{
- struct pmodad1_device *dev;
-
- dev = container_of(inode->i_cdev, struct pmodad1_device, cdev);
- fp->private_data = dev;
-
- return 0;
-}
-
-static const struct file_operations pmodad1_cdev_fops = {
- .owner = THIS_MODULE,
- .open = pmodad1_open,
- .read = pmodad1_read,
-};
-
-/**
- * add_pmodad1_device_to_bus - Add device to SPI bus, initialize SPI data.
- * @dev: pointer to device tree node
- *
- * This function adds device to SPI bus, initialize SPI data.
- */
-static int add_pmodad1_device_to_bus(struct pmodad1_device *dev)
-{
- struct spi_master *spi_master;
- struct spi_device *spi_device;
- int status = 0;
-
- spi_master = spi_busnum_to_master(dev->spi_id);
- if (!spi_master) {
- dev_err(&dev->pdev->dev, "spi_busnum_to_master(%d) returned NULL\n", dev->spi_id);
- return -ENOSYS;
- }
-
- spi_device = spi_alloc_device(spi_master);
- if (!spi_device) {
- put_device(&spi_master->dev);
- dev_err(&dev->pdev->dev, "spi_alloc_device() failed\n");
- return -ENOMEM;
- }
-
- spi_device->chip_select = 0;
- spi_device->max_speed_hz = dev->spi_speed;
-/* spi_device->max_speed_hz = 625000; */
- spi_device->mode = SPI_MODE_0;
- spi_device->bits_per_word = 8;
- spi_device->controller_data = (void *)dev->iCS;
- spi_device->dev.platform_data = dev;
- strlcpy(spi_device->modalias, SPI_DRIVER_NAME, sizeof(SPI_DRIVER_NAME));
-
- status = spi_add_device(spi_device);
- if (status < 0) {
- spi_dev_put(spi_device);
- dev_err(&dev->pdev->dev, "spi_add_device() failed %d\n", status);
- return status;
- }
- dev->spi = spi_device;
-
- put_device(&spi_master->dev);
- pr_info(DRIVER_NAME " SPI initialized, max_speed_hz\t%d\n", spi_device->max_speed_hz);
- return status;
-}
-
-/**
- * pmodad1_setup_cdev - Setup Char Device for ZED PmodAD1 device.
- * @dev: pointer to device tree node
- * @dev_id: pointer to device major and minor number
- * @spi: pointer to spi_device structure
- *
- * This function initializes char device for PmodAD1 device, and add it into
- * kernel device structure. It returns 0, if the cdev is successfully
- * initialized, or a negative value if there is an error.
- */
-static int pmodad1_setup_cdev(struct pmodad1_device *dev, dev_t *dev_id, struct spi_device *spi)
-{
- int status = 0;
- struct device *device;
-
- cdev_init(&dev->cdev, &pmodad1_cdev_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &pmodad1_cdev_fops;
- dev->spi = spi;
-
- *dev_id = MKDEV(MAJOR(pmodad1_dev_id), cur_minor++);
- status = cdev_add(&dev->cdev, *dev_id, 1);
- if (status < 0)
- return status;
-
- /* Add Device node in system */
- device = device_create(pmodad1_class, NULL,
- *dev_id, NULL,
- "%s", dev->name);
- if (IS_ERR(device)) {
- status = PTR_ERR(device);
- dev_err(&spi->dev, "failed to create device node %s, err %d\n",
- dev->name, status);
- cdev_del(&dev->cdev);
- }
-
- return status;
-}
-
-/**
- * SPI hardware probe. Sets correct SPI mode, attempts
- * to obtain memory needed by the driver, and performs
- * a simple initialization of the device.
- * @spi : pointer to spi device being initialized.
- */
-static int pmodad1_spi_probe(struct spi_device *spi)
-{
- int status = 0;
- struct pmodad1_device *pmodad1_dev;
-
- if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
- status = -EINVAL;
- pr_info(SPI_DRIVER_NAME "SPI settings incorrect: %d\n", status);
- goto spi_err;
- }
-
- /* use SPI_MODE_0 */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
-
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
- spi->mode, spi->max_speed_hz / 1000,
- status);
- goto spi_err;
- }
-
- /* Get pmodad1_device structure */
- pmodad1_dev = (struct pmodad1_device *)spi->dev.platform_data;
- if (pmodad1_dev == NULL) {
- dev_err(&spi->dev, "Cannot get pmodad1_device.\n");
- status = -EINVAL;
- goto spi_platform_data_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", pmodad1_dev->name);
-#endif
-
- /* Setup char driver */
- status = pmodad1_setup_cdev(pmodad1_dev, &(pmodad1_dev->dev_id), spi);
- if (status) {
- pr_info(" spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
- dev_err(&spi->dev, "spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
- goto cdev_add_err;
- }
-
- /* Initialize Mutex */
- mutex_init(&pmodad1_dev->mutex);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", pmodad1_dev->name);
-#endif
-
- return status;
-
-cdev_add_err:
-spi_platform_data_err:
-spi_err:
- return status;
-}
-
-/**
- * pmodad1_spi_remove - SPI hardware remove.
- * Performs tasks required when SPI is removed.
- * @spi : pointer to spi device being removed
- */
-static int pmodad1_spi_remove(struct spi_device *spi)
-{
- int status;
- struct pmodad1_device *dev;
-
- dev = (struct pmodad1_device *)spi->dev.platform_data;
-
- if (dev == NULL) {
- dev_err(&spi->dev, "spi_remove: Error fetch pmodad1_device struct\n");
- return -EINVAL;
- }
-
- if (&dev->cdev) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
-#endif
- device_destroy(pmodad1_class, dev->dev_id);
- cdev_del(&dev->cdev);
- }
-
- cur_minor--;
-
- return status;
-}
-
-static struct spi_driver pmodad1_spi_driver = {
- .driver = {
- .name = SPI_DRIVER_NAME,
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = pmodad1_spi_probe,
- .remove = pmodad1_spi_remove,
-};
-
-static const struct of_device_id pmodad1_of_match[] = {
- { .compatible = "dglnt,pmodad1", },
- {},
-};
-MODULE_DEVICE_TABLE(of, pmodad1_of_match);
-
-/**
- * pmodad1_of_probe - Probe method for PmodAD1 device (over GPIO).
- * @pdev: pointer to platform devices
- *
- * This function probes the PmodAD1 device in the device tree. It initializes the
- * PmodAD1 driver data structure. It returns 0, if the driver is bound to the PmodAD1
- * device, or a negative value if there is an error.
- */
-static int pmodad1_of_probe(struct platform_device *pdev)
-{
- struct pmodad1_device *pmodad1_dev;
- struct platform_device *pmodad1_pdev;
- struct spi_gpio_platform_data *pmodad1_pdata;
-
- struct device_node *np = pdev->dev.of_node;
-
- const u32 *tree_info;
- const u32 *spi_speed;
- int status = 0;
-
- /* Alloc Space for platform device structure */
- pmodad1_dev = kzalloc(sizeof(*pmodad1_dev), GFP_KERNEL);
- if (!pmodad1_dev) {
- status = -ENOMEM;
- dev_err(&pdev->dev, "Platform device structure allocation failed: %d\n", status);
- goto dev_alloc_err;
- }
-
- pmodad1_dev->val_buf = kmalloc(read_buf_size, GFP_KERNEL);
- if (!pmodad1_dev->val_buf) {
- status = -ENOMEM;
- dev_err(&pdev->dev, "Device value buffer allocation failed: %d\n", status);
- goto buf_alloc_err;
- }
-
- /* Get the GPIO Pins */
-
- pmodad1_dev->iSCLK = of_get_named_gpio(np, "spi-sclk-gpio", 0);
- pmodad1_dev->iSDOUT = of_get_named_gpio(np, "spi-sdout-gpio", 0);
- status = of_get_named_gpio(np, "spi-cs-gpio", 0);
- pmodad1_dev->iCS = (status < 0) ? SPI_GPIO_NO_CHIPSELECT : status;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: iSCLK: 0x%lx\n", np->name, pmodad1_dev->iSCLK);
- pr_info(DRIVER_NAME " %s: iSDOUT: 0x%lx\n", np->name, pmodad1_dev->iSDOUT);
- pr_info(DRIVER_NAME " %s: iCS : 0x%lx\n", np->name, pmodad1_dev->iCS);
-#endif
-
- /* Get SPI Related Params */
- tree_info = of_get_property(np, "spi-bus-num", NULL);
- if (tree_info) {
- pmodad1_dev->spi_id = be32_to_cpup((tree_info));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: BUS_ID\t%x\n", np->name, pmodad1_dev->spi_id);
-#endif
- }
-
- spi_speed = of_get_property(np, "spi-speed-hz", NULL);
- if (spi_speed) {
- pmodad1_dev->spi_speed = be32_to_cpup((spi_speed));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: SPI_SPEED\t%x\n", np->name, pmodad1_dev->spi_speed);
-#endif
- } else {
- pmodad1_dev->spi_speed = DEFAULT_SPI_SPEED;
- }
-
- /* Alloc Space for platform data structure */
- pmodad1_pdata = kzalloc(sizeof(*pmodad1_pdata), GFP_KERNEL);
- if (!pmodad1_pdata) {
- status = -ENOMEM;
- goto pdata_alloc_err;
- }
-
- /* Fill up Platform Data Structure */
- pmodad1_pdata->sck = pmodad1_dev->iSCLK;
- pmodad1_pdata->miso = pmodad1_dev->iSDOUT;
- pmodad1_pdata->mosi = SPI_GPIO_NO_MOSI;
- pmodad1_pdata->num_chipselect = 1;
-
- /* Alloc Space for platform data structure */
- pmodad1_pdev = kzalloc(sizeof(*pmodad1_pdev), GFP_KERNEL);
- if (!pmodad1_pdev) {
- status = -ENOMEM;
- goto pdev_alloc_err;
- }
-
- /* Fill up Platform Device Structure */
- pmodad1_pdev->name = "spi_gpio";
- pmodad1_pdev->id = pmodad1_dev->spi_id;
- pmodad1_pdev->dev.platform_data = pmodad1_pdata;
- pmodad1_dev->pdev = pmodad1_pdev;
-
- /* Register spi_gpio master */
- status = platform_device_register(pmodad1_dev->pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "platform_device_register failed: %d\n", status);
- goto pdev_reg_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi_gpio platform device registered.\n", np->name);
-#endif
- pmodad1_dev->name = (char *)np->name;
-
- /* Fill up Board Info for SPI device */
- status = add_pmodad1_device_to_bus(pmodad1_dev);
- if (status < 0) {
- dev_err(&pdev->dev, "add_pmodad1_device_to_bus failed: %d\n", status);
- goto spi_add_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi device registered.\n", np->name);
-#endif
-
- /* Point device node data to pmodad1_device structure */
- if (np->data == NULL)
- np->data = pmodad1_dev;
-
- if (pmodad1_dev_id == 0) {
- /* Alloc Major & Minor number for char device */
- status = alloc_chrdev_region(&pmodad1_dev_id, 0, MAX_PMODAD1_DEV_NUM, DRIVER_NAME);
- if (status) {
- dev_err(&pdev->dev, "Character device region not allocated correctly: %d\n", status);
- goto err_alloc_chrdev_region;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Char Device Region Registered, with Major: %d.\n",
- MAJOR(pmodad1_dev_id));
-#endif
- }
-
- if (pmodad1_class == NULL) {
- /* Create Pmodad1 Device Class */
- pmodad1_class = class_create(THIS_MODULE, DRIVER_NAME);
- if (IS_ERR(pmodad1_class)) {
- status = PTR_ERR(pmodad1_class);
- goto err_create_class;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : pmodad1 device class registered.\n");
-#endif
- }
-
- if (spi_drv_registered == 0) {
- /* Register SPI Driver for Pmodad1 Device */
- status = spi_register_driver(&pmodad1_spi_driver);
- if (status < 0) {
- dev_err(&pdev->dev, "pmodad1_spi_driver register failed: %d\n", status);
- goto err_spi_register;
- }
- spi_drv_registered = 1;
- }
-
- device_num++;
-
- return status;
-
-err_spi_register:
- class_destroy(pmodad1_class);
- pmodad1_class = NULL;
-err_create_class:
- unregister_chrdev_region(pmodad1_dev_id, MAX_PMODAD1_DEV_NUM);
- pmodad1_dev_id = 0;
-err_alloc_chrdev_region:
- spi_unregister_device(pmodad1_dev->spi);
-spi_add_err:
- platform_device_unregister(pmodad1_dev->pdev);
-pdev_reg_err:
- kfree(pmodad1_pdev);
-pdev_alloc_err:
- kfree(pmodad1_pdata);
-pdata_alloc_err:
- kfree(pmodad1_dev->val_buf);
-buf_alloc_err:
- kfree(pmodad1_dev);
-dev_alloc_err:
- return status;
-}
-
-/**
- * pmodad1_of_remove - Remove method for ZED PmodAD1 device.
- * @np: pointer to device tree node
- *
- * This function removes the PmodAD1 device in the device tree. It frees the
- * PmodAD1 driver data structure. It returns 0, if the driver is successfully
- * removed, or a negative value if there is an error.
- */
-static int pmodad1_of_remove(struct platform_device *pdev)
-{
- struct pmodad1_device *pmodad1_dev;
- struct device_node *np = pdev->dev.of_node;
-
- if (np->data == NULL) {
- dev_err(&pdev->dev, "pmodad1 %s: ERROR: No pmodad1_device structure found!\n", np->name);
- return -ENOSYS;
- }
- pmodad1_dev = (struct pmodad1_device *)(np->data);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Free display buffer.\n", np->name);
-#endif
-
- if (pmodad1_dev->val_buf != NULL)
- kfree(pmodad1_dev->val_buf);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Unregister gpio_spi Platform Devices.\n", np->name);
-#endif
-
- if (pmodad1_dev->pdev != NULL)
- platform_device_unregister(pmodad1_dev->pdev);
-
- np->data = NULL;
- device_num--;
-
- /* Unregister SPI Driver, Destroy pmodad1 class, Release device id Region after
- * all pmodad1 devices have been removed.
- */
- if (device_num == 0) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Unregister SPI Driver.\n");
-#endif
- spi_unregister_driver(&pmodad1_spi_driver);
- spi_drv_registered = 0;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Destroy pmodad1 Class.\n");
-#endif
-
- if (pmodad1_class)
- class_destroy(pmodad1_class);
- pmodad1_class = NULL;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Release Char Device Region.\n");
-#endif
-
- unregister_chrdev_region(pmodad1_dev_id, MAX_PMODAD1_DEV_NUM);
- pmodad1_dev_id = 0;
- }
-
- return 0;
-}
-
-static struct platform_driver pmodad1_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = pmodad1_of_match,
- },
- .probe = pmodad1_of_probe,
- .remove = pmodad1_of_remove,
-};
-
-module_platform_driver(pmodad1_driver);
-
-MODULE_AUTHOR("Cristian Fatu");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_NAME ": PmodAD1 driver");
-MODULE_ALIAS(DRIVER_NAME);
+++ /dev/null
-/*
- * pmodclp.c - Digilent PmodCLP driver
- *
- * Copyright (c) 2012 Digilent. All right reserved.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <asm/uaccess.h>
-
-#define DRIVER_NAME "pmodclp"
-#define MAX_PMODCLP_DEV_NUM 16
-#define TXT_BUF_SIZE 1024
-#define MAX_NO_ROWS 2 /* The device has 2 rows */
-#define MAX_NO_COLS 40 /* The device has max 40 columns */
-
-#define CMD_LCDFNCINIT 0x38 /* function set command, (8-bit interface, 2 lines, and 5x8 dots) */
-#define CMD_LCDCTLINIT 0x08 /* display control set command */
-#define CMD_LCDCLEAR 0x01 /* clear display command */
-#define CMD_LCDRETHOME 0x02 /* return home command */
-#define CMD_LCDDISPLAYSHIFT 0x18 /* shift display command */
-#define CMD_LCDCURSORSHIFT 0x10 /* shift cursor command */
-#define CMD_LCDSETDDRAMPOS 0x80 /* set DDRAM position command */
-#define CMD_LCDSETCGRAMPOS 0x40 /* set CGRAM position command */
-
-#define MSK_BSTATUS 0x80 /* bit busy */
-#define MSK_SHIFTRL 0x04 /* shift direction mask */
-#define OPT_DISPLAYON 0x4 /* Set Display On option */
-#define OPT_CURSORON 0x2 /* Set Cursor On option */
-#define OPT_BLINKON 0x1 /* Set Blink On option */
-
-static dev_t pmodclp_dev_id;
-static unsigned int device_num;
-static unsigned int cur_minor;
-static struct class *pmodclp_class;
-
-/* structure that keeps the parallel port related information */
-struct par_device {
- /* Pin Assignment */
-
- unsigned long iRS;
- unsigned long iRW;
- unsigned long iE;
- unsigned long iBK;
- unsigned long iData[8];
-};
-
-struct pmodclp_device {
- char *name;
- /* R/W Mutex Lock */
- struct mutex mutex;
- /* Text Buffer */
- char *txt_buf; /* Device Text buffer */
- unsigned long cur_row; /* Maintain current row */
- int exceeded_rows; /* Flag for situation where maximum number of rows is exceeded */
-
- int display_on;
- int cursor_on;
- int blink_on;
- int bk_on;
-
- /* Pin Assignment */
- struct par_device par_dev;
-
- /* Char Device */
- struct cdev cdev;
- dev_t dev_id;
-};
-
-/* Forward definitions */
-void parse_text(char *txt_buf, int cnt, struct pmodclp_device *dev);
-void pmodclp_write_command(struct par_device *par_dev, unsigned char cmd);
-static int pmodclp_init_gpio(struct pmodclp_device *pmodclp_dev);
-
-/**
- * A basic open function.
- */
-static int pmodclp_open(struct inode *inode, struct file *fp)
-{
- struct pmodclp_device *dev;
-
- dev = container_of(inode->i_cdev, struct pmodclp_device, cdev);
- fp->private_data = dev;
-
- return 0;
-}
-
-/**
- * A basic close function, do nothing.
- */
-
-static int pmodclp_close(struct inode *inode, struct file *fp)
-{
- return 0;
-}
-
-/*
- * Driver write function
- *
- * This function uses a generic SPI write to send values to the Pmod device.
- * It takes a string from the app in the buffer.
- * It interprets the string of characters, and sends the commands and the text to PmodCLP over the parallel interface.
- */
-
-static ssize_t pmodclp_write(struct file *fp, const char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- int cnt;
- struct pmodclp_device *dev;
-
- dev = fp->private_data;
-
- if (mutex_lock_interruptible(&dev->mutex)) {
- retval = -ERESTARTSYS;
- goto write_lock_err;
- }
-
- cnt = length;
-
- if (copy_from_user(dev->txt_buf, buffer, cnt)) {
- retval = -EFAULT;
- goto quit_write;
- }
- retval = cnt;
-
- dev->txt_buf[cnt] = '\0';
-
- parse_text(dev->txt_buf, cnt, dev);
-
-quit_write:
- mutex_unlock(&dev->mutex);
-write_lock_err:
- return retval;
-}
-
-static void write_display_control_cmd(struct pmodclp_device *dev)
-{
- unsigned char cmd = CMD_LCDCTLINIT +
- (dev->display_on ? OPT_DISPLAYON : 0) +
- (dev->cursor_on ? OPT_CURSORON : 0) +
- (dev->blink_on ? OPT_BLINKON : 0);
-
- pmodclp_write_command(&(dev->par_dev), cmd);
-
-}
-
-/* Begin of parallel interface functions */
-
-/**
- * gpio_par_define_data_direction - Configure the gpio data pins as input or output.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- * @bool fOutput: true if the pins are configured as output
- false if the pins are configured as input
- * @unsigned char bOutputVal the 8 bit value corresponding to the initial value set for the 8 lines if they are defined as output
- *
- *
- * This function configures the gpio data pins as input or output, as required by read or write operations.
- */
-static int gpio_par_define_data_direction(struct par_device *par_dev, bool fOutput, unsigned char bOutputVal)
-{
- int i;
- int status = 0;
-
- for (i = 0; !status && (i < 8); i++) {
- if (fOutput)
- status = gpio_direction_output(par_dev->iData[i], ((bOutputVal & (1 << i)) ? 1 : 0));
- else
- status = gpio_direction_input(par_dev->iData[i]);
- }
- udelay(20);
- return status;
-}
-
-/**
- * gpio_par_read_byte - Read one byte function.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- *
- * Return value the byte read
- *
- * This function implements the parallel read cycle on the gpio pins. It is the basic read function.
- */
-static unsigned char gpio_par_read_byte(struct par_device *par_dev)
-{
- int i;
- unsigned char bData = 0;
-
- /* Set RW */
- gpio_set_value(par_dev->iRW, 1);
- udelay(20);
- /* Set Enable */
- gpio_set_value(par_dev->iE, 1);
- udelay(20);
- for (i = 0; i < 8; i++)
- bData += ((unsigned char)gpio_get_value(par_dev->iData[i]) << i);
-
- /* Clear Enable */
- gpio_set_value(par_dev->iE, 0);
- udelay(20);
- /* Clear RW */
- gpio_set_value(par_dev->iRW, 0);
- udelay(20);
- return bData;
-}
-
-/**
- * gpio_par_write_byte - Write one byte function.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- * @unsigned char bData: the byte to be written over the parallel interface.
- *
- *
- * This function implements the parallel write cycle on the gpio pins. It is the basic write function,
- * it writes one byte over the parallel interface.
- */
-static void gpio_par_write_byte(struct par_device *par_dev, unsigned char bData)
-{
- int i;
-
- /* Clear RW */
- gpio_set_value(par_dev->iRW, 0);
- udelay(20);
- /* Set Enable */
- gpio_set_value(par_dev->iE, 1);
- udelay(20);
- for (i = 0; i < 8; i++)
- gpio_set_value(par_dev->iData[i], (bData >> i) & 0x01);
-
- /* Clear Enable */
- gpio_set_value(par_dev->iE, 0);
- udelay(20);
- /* Set RW */
- gpio_set_value(par_dev->iRW, 1);
- udelay(20);
-}
-
-/**
- * pmodclp_read_status - Read Status function
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- *
- *
- * This function reads the status of the PmodCLP device.
- */
-static unsigned char pmodclp_read_status(struct par_device *par_dev)
-{
- unsigned char bStatus;
-
- /* define data pins as input */
- gpio_par_define_data_direction(par_dev, false, 0);
-
- /* clear RS, meaning instruction register */
- gpio_set_value(par_dev->iRS, 0);
- udelay(20);
-
- bStatus = gpio_par_read_byte(par_dev);
-
- return bStatus;
-}
-
-/**
- * pmodclp_wait_until_not_busy - Wait until device is ready function
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- *
- *
- * This function loops until the device reports to be not busy.
- */
-static void pmodclp_wait_until_not_busy(struct par_device *par_dev)
-{
- unsigned char bStatus;
-
- /* read status */
- bStatus = pmodclp_read_status(par_dev);
- while (bStatus & MSK_BSTATUS) {
- mdelay(10);
- bStatus = pmodclp_read_status(par_dev);
- }
-}
-
-/**
- * gpio_par_write_byte - Write one command byte function.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- * @unsigned char bData: the byte containing the command to be written over the parallel interface.
- *
- *
- * This function writes a command byte over the parallel interface.
- */
-void pmodclp_write_command(struct par_device *par_dev, unsigned char cmd)
-{
- /* wait until LCD is not busy */
- pmodclp_wait_until_not_busy(par_dev);
-
- /* clear RS, meaning instruction register */
- gpio_set_value(par_dev->iRS, 0);
- udelay(20);
-
- /* Write command byte */
- /* define data pins as output, and provide initial output value */
- gpio_par_define_data_direction(par_dev, true, cmd);
-
- /* implement write command */
- gpio_par_write_byte(par_dev, cmd);
-}
-
-/**
- * gpio_par_write_byte - Write array of caracters as data function.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- * @char *txt_buf: the text array to be written
- * @int cnt the number of charcaters in the text array
- *
- * This function writes a number of characters as data over the parallel interface.
- */
-static void pmodclp_write_data(struct par_device *par_dev, char *txt_buf, int cnt)
-{
- int i;
-
- /* set RS, meaning data */
- gpio_set_value(par_dev->iRS, 1);
- udelay(20);
-
- /* define data pins as output, and provide initial output value */
- gpio_par_define_data_direction(par_dev, true, txt_buf[0]);
- for (i = 0; i < cnt; i++)
- /* implement write command */
- gpio_par_write_byte(par_dev, txt_buf[i]);
-}
-
-/**
- * pmodclp_init - Required initialization sequence for PmodCLP.
- *
- * Parameters:
- * @struct par_device *par_dev: pointer to the structure containing parallel interface information
- *
- *
- * This function performs the required initialization sequence for PmodCLP. See the reference manual for more information.
- */
-static void pmodclp_init(struct par_device *par_dev)
-{
-
- /* perform initialization sequence, according to datasheet */
-
- /* wait 20 ms */
- mdelay(20);
- /* Set function */
- pmodclp_write_command(par_dev, CMD_LCDFNCINIT);
- /* Wait 37 us */
- udelay(37);
-
- /* display on, no cursor, no blinking */
- pmodclp_write_command(par_dev, CMD_LCDCTLINIT);
-
- /* Wait 37 us */
- udelay(37);
-
- /* Display Clear */
- pmodclp_write_command(par_dev, CMD_LCDCLEAR);
- /* Wait 1.52 ms */
- udelay(1520);
-
-}
-/* Begin of parse functions */
-
-/**
- * is_decimal_digit - This function returns true if the specified character is among decimal characters.
- *
- * Parameters
- * @char c: character that is searched to be among decimal characters
- *
- * Return value
- * true if the specified character is among decimal characters
- * false if the character character is not among decimal characters
- *
- * This function returns true if the specified character is among hexa characters ('0', '1', ...'9').
- */
-static bool is_decimal_digit(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-/**
- * is_hexa_digit - This function returns true if the specified character is among hexa characters.
- *
- * Parameters
- * @char c: character that is searched to be among hexa characters
- *
- * Return value
- * true if the specified character is among hexa characters
- * false if the character character is not among hexa characters
- *
- * This function returns true if the specified character is among hexa characters ('0', '1', ...'9', 'A', 'B', ... , 'F', 'a', 'b', ..., 'f').
- */
-static bool is_hexa_digit(char c)
-{
- return is_decimal_digit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
-}
-
-/**
- * is_binary_digit - This function returns true if the specified character is among binary characters.
- *
- * Parameters
- * @char c: character that is searched to be among binary characters
- *
- * Return value
- * true if the specified character is among binary characters
- * false if the character character is not among binary characters
- *
- * This function returns true if the specified character is among binary characters ('0' and '1').
- */
-static bool is_binary_digit(char c)
-{
- return c == '0' || c == '1';
-}
-
-/**
- * parse_cmd - This function builds the commands to be sent for each recognized escape sequence.
- *
- * Parameters
- * @char c: character that is searched to be among command codes
- * @unsigned char *pars parameters array, built in parse_text function
- * @int idx_par index of last parameter in parameters array, built in parse_text function
- * @int par_typ the type of the last parameter
- * 0 - decimal
- * 1 - hexa
- * 2 - binary
- * @struct pmodclp_device *dev pointer to device structure
- *
- * Return value
- * 1 if the character was recognized as a command code and the parameters configuration is correct for that command
- * 0 if the character is not a command code
- *
- * This function tries to mach the specified character and the parameters configuration to a command.
- * If it does, the command is built and sent to PmodCLP on parallel interface and 1 is returned.
- * If no command is recognized for the character, 0 is returned.
- */
-
-static int parse_cmd(char c, unsigned char *pars, int idx_par, int par_type, struct pmodclp_device *dev)
-{
- int is_consumed_char = 0; /* this will be returned */
- unsigned char cmd;
-
- switch (c) {
- case 'e':
- /* enable/disable display */
- if (idx_par >= 0 && par_type == 0 && pars[0] >= 0 && pars[0] <= 3) {
- /* set display */
- int display, bk;
- display = (pars[0] & 1) != 0;
- if (display != dev->display_on) {
- dev->display_on = display;
- write_display_control_cmd(dev);
- }
-
- /* set background light, if the pin is defined. */
- if (dev->par_dev.iBK != -1) {
- bk = (pars[0] & 2) >> 1;
- if (bk != dev->bk_on) {
- dev->bk_on = bk;
- gpio_set_value(dev->par_dev.iBK, bk);
- }
- }
- is_consumed_char = 1; /* mark char as consumed. */
- }
- break;
- case 'H': /* set cursor position */
- if (idx_par == 1 && pars[0] >= 0 && pars[0] < MAX_NO_ROWS && pars[1] >= 0 && pars[1] < MAX_NO_COLS) {
- /* allow only decimal parameter */
- dev->cur_row = pars[0];
- dev->exceeded_rows = 0;
- cmd = 0x40 * pars[0] + pars[1];
- cmd |= CMD_LCDSETDDRAMPOS;
- pmodclp_write_command(&(dev->par_dev), cmd);
- is_consumed_char = 1; /* mark char as consumed. */
- }
- break;
- case 'j': /* clear display and home cursor */
- dev->cur_row = 0;
- dev->exceeded_rows = 0;
- pmodclp_write_command(&(dev->par_dev), CMD_LCDCLEAR);
- is_consumed_char = 1; /* mark char as consumed. */
- break;
- case '@': /* scroll left */
- if (idx_par == 0 && pars[0] >= 0 && pars[0] < MAX_NO_COLS) {
- /* allow only decimal parameter */
- int i;
- cmd = CMD_LCDDISPLAYSHIFT;
- for (i = 0; i < pars[0]; i++)
- /* scroll one position */
- pmodclp_write_command(&(dev->par_dev), cmd);
- is_consumed_char = 1; /* mark char as consumed. */
- }
- break;
- case 'A': /* scroll right */
- if (idx_par == 0 && pars[0] >= 0 && pars[0] < MAX_NO_COLS) {
- /* allow only decimal parameter */
- int i;
- cmd = CMD_LCDDISPLAYSHIFT | MSK_SHIFTRL;
- for (i = 0; i < pars[0]; i++)
- /* scroll one position */
- pmodclp_write_command(&(dev->par_dev), cmd);
- is_consumed_char = 1; /* mark char as consumed. */
- }
- break;
- case 'c': /* set cursor mode */
- if (idx_par == 0 && par_type == 0 && pars[0] >= 0 && pars[0] <= 2) {
- /* allow only decimal parameter */
- int cursor, blink;
-
- /* set cursor */
- cursor = pars[0] >= 1; /* 1 and 2 */
- if (cursor != dev->cursor_on) {
- dev->cursor_on = cursor;
- write_display_control_cmd(dev);
- }
- /* set blink */
- blink = pars[0] <= 1; /* 0 and 1 */
-
- if (blink != dev->blink_on) {
- dev->blink_on = blink;
- write_display_control_cmd(dev);
- }
- is_consumed_char = 1; /* mark char as consumed. */
- }
- break;
- case 'd': /* define user programmable character */
- {
- /* define user char */
- if (idx_par == 8 && par_type == 0) {
- /* 9 params
- - first 8 definition bytes and
- - last one is the character number, allow only ecimal character
- */
-
- /* set CGRAM pos */
- unsigned char cmd = CMD_LCDSETCGRAMPOS | (pars[8] << 3);
- pmodclp_write_command(&(dev->par_dev), cmd);
-
- /* write 8 character definition bytes as data */
- pmodclp_write_data(&(dev->par_dev), pars, 8);
- is_consumed_char = 1; /* mark} char as consumed. */
- }
- }
- break;
- default:
- break;
- /* no command was recognized */
- }
- return is_consumed_char;
-}
-
-/**
- * parse_text - This function builds the commands to be sent for each recognized escape sequence.
- *
- * Parameters
- * @char *txt_buf: the text array to be parsed
- * @int cnt the number of charcaters to be parsed in the text array
- * @struct pmodclp_device *dev pointer to device structure
- *
- *
- * This function parses a text array, containing a sequence of one or more text or commands sent to PmodCLP. Its purpose is:
- * - recognize, interpret the text sent to the device:
- * - split the separate commands / text and process them individually.
- * - recognize escape code commands and translate them into PmodCLP commands on parallel interface
- * - send text data to PmodCLP device on parallel interface
- * - maintain a shadow value of the current row (this is because the cursor position cannot be read)
- * - recognize LF character ('\n') inside a text to be sent to the device
- * - if current line is the first, move the cursor to the beginning of the next line
- * - if current line is the second, there is no room for new line. Text characters after LF are ignored, commands are still interpreted.
- *
- */
-void parse_text(char *txt_buf, int cnt, struct pmodclp_device *dev)
-{
- int is_ignore_txt;
- int is_cmd = 0;
- int is_inside_par = -1;
- int is_consumed_char;
- int par_type = 0;
-
- char *parse_ptr, *processed_ptr, *par_ptr;
- int idx_par = -1;
- unsigned char pars[10];
-
- par_ptr = NULL;
- parse_ptr = txt_buf;
- processed_ptr = txt_buf - 1;
- is_cmd = 0;
- par_type = -1;
- is_ignore_txt = dev->exceeded_rows;
- while (parse_ptr < (txt_buf + cnt)) {
- is_consumed_char = 0; /* waiting to be consumed */
- /* recognize command - look for ESC code, followed by '[' */
- if ((!is_cmd) && ((*parse_ptr) == 0x1B) && (parse_ptr[1] == '[')) {
- /* enter command mode */
- is_cmd = 1;
- is_inside_par = 0; /* able to receive the parameter */
- /* send previous text (before the ESC sequence) */
- if ((parse_ptr - processed_ptr) > 1)
- pmodclp_write_data(&(dev->par_dev), processed_ptr + 1, parse_ptr - 1 - processed_ptr);
- parse_ptr++; /* skip '[' char */
- } else {
- if (is_cmd) {
- /* look for commands */
- if (!(par_type == 1 && (parse_ptr - par_ptr) <= 2))
- /* do not look for commands when current parameter is hexa and less than 2 chars are parsed */
- is_consumed_char = parse_cmd(*parse_ptr, pars, idx_par, par_type, dev);
- is_ignore_txt = dev->exceeded_rows; /* because command parsing may change this parameter */
- if (is_consumed_char) {
- /* mark text as processed including the command char */
- if ((parse_ptr - processed_ptr) > 0) {
- processed_ptr = parse_ptr;
- is_cmd = 0; /* comand mode is abandonned */
- }
- }
- if (!is_inside_par) {
- /* look for begining of a parameter */
- if (is_decimal_digit(*parse_ptr)) {
- par_type = -1;
- if (*parse_ptr == '0') {
- if (parse_ptr[1] == 'x' || parse_ptr[1] == 'X') {
- /* 0x or 0X sequence detected, start a hexa parameter */
- par_type = 1;
- is_consumed_char = 1; /* char was consumed */
- parse_ptr++; /* skip 'x' or 'X' char */
- } else {
- if (parse_ptr[1] == 'b' || parse_ptr[1] == 'B') {
- /* 0B or 0b sequence detected, start a binary parameter */
- par_type = 2;
- is_consumed_char = 1; /* char was consumed */
- parse_ptr++; /* skip 'b' or 'B' char */
- }
- }
- }
- idx_par++;
- if (!is_consumed_char) {
- /* 0x or 0b were not detected, start a decimal parameter */
- par_type = 0;
- pars[idx_par] = (*parse_ptr - '0');
- is_consumed_char = 1; /* char was consumed */
- } else {
- pars[idx_par] = 0;
- }
- par_ptr = parse_ptr;
- is_inside_par = 1;
- }
- } else {
- /* inside parameter, look for ';' separator and parameter digits */
- if (!is_consumed_char) {
- if ((*parse_ptr) == ';') { /* parameters separator */
- par_ptr = NULL;
- is_inside_par = 0; /* look for a new parameter */
- is_consumed_char = 1; /* char was consumed */
- }
- }
- if (!is_consumed_char) {
- switch (par_type) {
- /* interpret parameter digit */
- case 0: /* decimal */
- if (is_decimal_digit(*parse_ptr)) {
- pars[idx_par] = 10 * pars[idx_par] + (*parse_ptr - '0');
- is_consumed_char = 1; /* char was consumed */
- }
- /* else wrong char, expecting decimal digit, do not consume char */
- break;
- case 1: /* hexa */
- if (is_hexa_digit(*parse_ptr)) {
- pars[idx_par] = pars[idx_par] << 4;
- if (*parse_ptr >= '0' && *parse_ptr <= '9')
- pars[idx_par] += (*parse_ptr - '0');
- else{
- if (*parse_ptr >= 'A' && *parse_ptr <= 'F')
- pars[idx_par] += 10 + (*parse_ptr - 'A');
- else
- pars[idx_par] += 10 + (*parse_ptr - 'a');
- }
- is_consumed_char = 1; /* char was consumed */
- }
- /* else wrong char, expecting decimal digit, do not consume char */
- break;
- case 2: /* binary */
- if (is_binary_digit(*parse_ptr)) {
- pars[idx_par] = (pars[idx_par] << 1) + (*parse_ptr - '0');
- is_consumed_char = 1; /* char was consumed */
- }
- /* else wrong char, expecting binary digit, leave command mode */
- break;
- }
- } /* end of interpret parameter digit */
- } /* end of parameter} */
- if (!is_consumed_char) {
- /* inside command mode, if character is not consumed, leave command mode */
- is_cmd = 0;
- /* consume unrecongnized command characters, makes no sense to display them on LCD */
- pr_info(" Wrong command: %.*s\n", (int)(parse_ptr - processed_ptr + 1), processed_ptr);
- processed_ptr = parse_ptr;
- }
- /* end of inside command */
- } else {
- /* free text, not inside a command */
- if (is_ignore_txt) {
- /* if text is ignored, processed_ptr advances together with parse_ptr */
- processed_ptr = parse_ptr;
- } else {
- if ((*parse_ptr) == '\n') { /* LF */
- /* mark processed before the LF char */
- if ((parse_ptr - processed_ptr) > 0)
- pmodclp_write_data(&(dev->par_dev), processed_ptr + 1, parse_ptr - 1 - processed_ptr);
- /* position the cursor on the beginning of the next line */
- if (dev->cur_row < (MAX_NO_ROWS - 1)) {
- dev->cur_row++;
- pmodclp_write_command(&(dev->par_dev), CMD_LCDSETDDRAMPOS + 0x40 * dev->cur_row);
- } else {
- /* there is no room to place a third line. Enter ignore text (still look for the comands) */
- is_ignore_txt = 1;
- }
- /* advance the pointers so that LF char is skipped next time when chars are sent */
- processed_ptr = parse_ptr;
- }
- }
- }
- }
- parse_ptr++; /* advance one character */
- }
- parse_ptr--;
- /* send remaining chars */
-
- if (((parse_ptr - processed_ptr) > 0)) {
- if (!is_cmd)
- pmodclp_write_data(&(dev->par_dev), processed_ptr + 1, parse_ptr - processed_ptr);
- else
- pr_info(" Wrong command: %.*s\n", (int)(parse_ptr - processed_ptr + 2), processed_ptr + 1);
- }
-
- dev->exceeded_rows = is_ignore_txt;
-}
-
-/**
- * Driver Read Function
- *
- * This function does not actually read the PmodCLP as it is a write-only device.
- */
-static ssize_t pmodclp_read(struct file *fp, char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
-
- return retval;
-}
-
-static const struct file_operations pmodclp_cdev_fops = {
- .owner = THIS_MODULE,
- .write = pmodclp_write,
- .read = pmodclp_read,
- .open = pmodclp_open,
- .release = pmodclp_close,
-};
-
-/**
- * pmodclp_setup_cdev - Setup Char Device for ZED PmodCLP device.
- * @dev: pointer to device tree node
- * @dev_id: pointer to device major and minor number
- * @spi: pointer to spi_device structure
- *
- * This function initializes char device for PmodCLP device, and add it into
- * kernel device structure. It returns 0, if the cdev is successfully
- * initialized, or a negative value if there is an error.
- */
-static int pmodclp_setup_cdev(struct pmodclp_device *dev, dev_t *dev_id)
-{
- int status = 0;
- struct device *device;
-
- cdev_init(&dev->cdev, &pmodclp_cdev_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &pmodclp_cdev_fops;
-
- *dev_id = MKDEV(MAJOR(pmodclp_dev_id), cur_minor++);
- status = cdev_add(&dev->cdev, *dev_id, 1);
- if (status < 0) {
- pr_info(" cdev_add failed ...\n");
- return status;
- }
-
- /* Add Device node in system */
- device = device_create(pmodclp_class, NULL,
- *dev_id, NULL,
- "%s", dev->name);
- if (IS_ERR(device)) {
- status = PTR_ERR(device);
- pr_info("failed to create device node %s, err %d\n",
- dev->name, status);
- cdev_del(&dev->cdev);
- }
-
- return status;
-}
-
-static const struct of_device_id pmodclp_of_match[] = {
- { .compatible = "dglnt,pmodclp", },
- {},
-};
-MODULE_DEVICE_TABLE(of, pmodclp_of_match);
-
-/**
- * pmodclp_of_probe - Probe method for PmodCLP device (over GPIO).
- * @pdev: pointer to platform devices
- *
- * This function probes the PmodCLP device in the device tree. It initializes the
- * PmodCLP driver data structure. It returns 0, if the driver is bound to the PmodCLP
- * device, or a negative value if there is an error.
- */
-static int pmodclp_of_probe(struct platform_device *pdev)
-{
- struct pmodclp_device *pmodclp_dev;
-
- struct device_node *np = pdev->dev.of_node;
-
- int status = 0;
-
- /* Alloc Space for platform device structure */
- pmodclp_dev = kzalloc(sizeof(*pmodclp_dev), GFP_KERNEL);
- if (!pmodclp_dev) {
- status = -ENOMEM;
- goto dev_alloc_err;
- }
-
- /* Alloc Text Buffer for device */
- pmodclp_dev->txt_buf = kmalloc(TXT_BUF_SIZE, GFP_KERNEL);
- if (!pmodclp_dev->txt_buf) {
- status = -ENOMEM;
- dev_err(&pdev->dev, "Device Display data buffer allocation failed: %d\n", status);
- goto txt_buf_alloc_err;
- }
-
- /* Get the GPIO Pins */
- pmodclp_dev->par_dev.iRS = of_get_named_gpio(np, "rs-gpio", 0);
- pmodclp_dev->par_dev.iRW = of_get_named_gpio(np, "rw-gpio", 0);
- pmodclp_dev->par_dev.iE = of_get_named_gpio(np, "e-gpio", 0);
- status = of_get_named_gpio(np, "bk-gpio", 0);
- pmodclp_dev->par_dev.iBK = (status < 0) ? -1 : status;
-
- pmodclp_dev->par_dev.iData[0] = of_get_named_gpio(np, "data0-gpio", 0);
- pmodclp_dev->par_dev.iData[1] = of_get_named_gpio(np, "data1-gpio", 0);
- pmodclp_dev->par_dev.iData[2] = of_get_named_gpio(np, "data2-gpio", 0);
- pmodclp_dev->par_dev.iData[3] = of_get_named_gpio(np, "data3-gpio", 0);
- pmodclp_dev->par_dev.iData[4] = of_get_named_gpio(np, "data4-gpio", 0);
- pmodclp_dev->par_dev.iData[5] = of_get_named_gpio(np, "data5-gpio", 0);
- pmodclp_dev->par_dev.iData[6] = of_get_named_gpio(np, "data6-gpio", 0);
- pmodclp_dev->par_dev.iData[7] = of_get_named_gpio(np, "data7-gpio", 0);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: iRS: 0x%lx\n", np->name, pmodclp_dev->par_dev.iRS);
- pr_info(DRIVER_NAME " %s: iRW: 0x%lx\n", np->name, pmodclp_dev->par_dev.iRW);
- pr_info(DRIVER_NAME " %s: iE : 0x%lx\n", np->name, pmodclp_dev->par_dev.iE);
- pr_info(DRIVER_NAME " %s: iBK : 0x%lx\n", np->name, pmodclp_dev->par_dev.iBK);
-
- pr_info(DRIVER_NAME " %s: iData[0] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[0]);
- pr_info(DRIVER_NAME " %s: iData[1] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[1]);
- pr_info(DRIVER_NAME " %s: iData[2] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[2]);
- pr_info(DRIVER_NAME " %s: iData[3] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[3]);
- pr_info(DRIVER_NAME " %s: iData[4] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[4]);
- pr_info(DRIVER_NAME " %s: iData[5] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[5]);
- pr_info(DRIVER_NAME " %s: iData[6] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[6]);
- pr_info(DRIVER_NAME " %s: iData[7] : 0x%lx\n", np->name, pmodclp_dev->par_dev.iData[7]);
-#endif
- pmodclp_dev->name = (char *)np->name;
-
- /* initialize device data */
- pmodclp_dev->cur_row = 0;
- pmodclp_dev->exceeded_rows = 0;
-
- pmodclp_dev->display_on = 0;
- pmodclp_dev->cursor_on = 0;
- pmodclp_dev->blink_on = 0;
- pmodclp_dev->bk_on = 0;
-
- /* Point device node data to pmodclp_device structure */
- if (np->data == NULL)
- np->data = pmodclp_dev;
-
- if (pmodclp_dev_id == 0) {
- /* Alloc Major & Minor number for char device */
- status = alloc_chrdev_region(&pmodclp_dev_id, 0, MAX_PMODCLP_DEV_NUM, DRIVER_NAME);
- if (status) {
- dev_err(&pdev->dev, "Character device region not allocated correctly: %d\n", status);
- goto err_alloc_chrdev_region;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Char Device Region Registered, with Major: %d.\n",
- MAJOR(pmodclp_dev_id));
-#endif
- }
-
- if (pmodclp_class == NULL) {
- /* Create Pmodclp Device Class */
- pmodclp_class = class_create(THIS_MODULE, DRIVER_NAME);
- if (IS_ERR(pmodclp_class)) {
- status = PTR_ERR(pmodclp_class);
- goto err_create_class;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : pmodclp device class registered.\n");
-#endif
- }
-
- /* Setup char driver */
- status = pmodclp_setup_cdev(pmodclp_dev, &(pmodclp_dev->dev_id));
- if (status) {
- pr_info(" pmodclp_probe: Error adding %s device: %d\n", DRIVER_NAME, status);
- goto cdev_add_err;
- }
-
- device_num++;
-
- /* Initialize Mutex */
- mutex_init(&pmodclp_dev->mutex);
-
- status = pmodclp_init_gpio(pmodclp_dev);
- if (status) {
- pr_info(" spi_probe: Error init gpio: %d\n", status);
- goto cdev_add_err;
- }
-
- pmodclp_init(&pmodclp_dev->par_dev);
-
- return status;
-
-err_create_class:
- unregister_chrdev_region(pmodclp_dev_id, MAX_PMODCLP_DEV_NUM);
- pmodclp_dev_id = 0;
-err_alloc_chrdev_region:
-cdev_add_err:
-
- pr_info(DRIVER_NAME " Free text buffer.\n");
-
- kfree(pmodclp_dev->txt_buf);
-txt_buf_alloc_err:
- kfree(pmodclp_dev);
-dev_alloc_err:
- return status;
-}
-
-/**
- * pmodclp_init_gpio - Initialize GPIO for ZED PmodCLP device
- * @dev - pmodclp_device
- *
- * Initializes PmodCLP GPIO Control Pins.
- * It returns 0, if the gpio pins are successfully
- * initialized, or a negative value if there is an error.
- */
-static int pmodclp_init_gpio(struct pmodclp_device *pmodclp_dev)
-{
- struct gpio pmodclp_ctrl[] = {
- { pmodclp_dev->par_dev.iRS, GPIOF_OUT_INIT_HIGH, "CLP RS" },
- { pmodclp_dev->par_dev.iRW, GPIOF_OUT_INIT_HIGH, "CLP RW" },
- { pmodclp_dev->par_dev.iE, GPIOF_OUT_INIT_HIGH, "CLP E" },
- { pmodclp_dev->par_dev.iData[0], GPIOF_OUT_INIT_HIGH, "CLP DATA[0]" },
- { pmodclp_dev->par_dev.iData[1], GPIOF_OUT_INIT_HIGH, "CLP DATA[1]" },
- { pmodclp_dev->par_dev.iData[2], GPIOF_OUT_INIT_HIGH, "CLP DATA[2]" },
- { pmodclp_dev->par_dev.iData[3], GPIOF_OUT_INIT_HIGH, "CLP DATA[3]" },
- { pmodclp_dev->par_dev.iData[4], GPIOF_OUT_INIT_HIGH, "CLP DATA[4]" },
- { pmodclp_dev->par_dev.iData[5], GPIOF_OUT_INIT_HIGH, "CLP DATA[5]" },
- { pmodclp_dev->par_dev.iData[6], GPIOF_OUT_INIT_HIGH, "CLP DATA[6]" },
- { pmodclp_dev->par_dev.iData[7], GPIOF_OUT_INIT_HIGH, "CLP DATA[7]" },
- { pmodclp_dev->par_dev.iBK, GPIOF_OUT_INIT_HIGH, "CLP BK" }
- };
- int status;
- int i;
- int array_size = pmodclp_dev->par_dev.iBK == -1 ? (ARRAY_SIZE(pmodclp_ctrl) - 1) : ARRAY_SIZE(pmodclp_ctrl);
-
- for (i = 0; i < array_size; i++) {
- status = gpio_is_valid(pmodclp_ctrl[i].gpio);
- if (!status) {
- pr_info("!! gpio_is_valid for GPIO %d, %s FAILED!, status: %d\n",
- pmodclp_ctrl[i].gpio, pmodclp_ctrl[i].label, status);
- goto gpio_invalid;
- }
- }
-
- pr_info("** gpio_request_array array_size = %d, ARRAY_SIZE = %d\n", array_size, (int)ARRAY_SIZE(pmodclp_ctrl));
-
- status = gpio_request_array(pmodclp_ctrl, ARRAY_SIZE(pmodclp_ctrl));
- if (status) {
- pr_info("!! gpio_request_array FAILED!\n");
- pr_info(" status is: %d, array_size = %d, ARRAY_SIZE = %d\n", status, array_size, (int)ARRAY_SIZE(pmodclp_ctrl));
- gpio_free_array(pmodclp_ctrl, 4);
- goto gpio_invalid;
- }
-
-gpio_invalid:
-/* gpio_direction_output_invalid: */
- return status;
-}
-
-/**
- * pmodclp_of_remove - Remove method for ZED PmodCLP device.
- * @np: pointer to device tree node
- *
- * This function removes the PmodCLP device in the device tree. It frees the
- * PmodCLP driver data structure. It returns 0, if the driver is successfully
- * removed, or a negative value if there is an error.
- */
-static int pmodclp_of_remove(struct platform_device *pdev)
-{
- struct pmodclp_device *pmodclp_dev;
- struct device_node *np = pdev->dev.of_node;
-
- if (np->data == NULL) {
- dev_err(&pdev->dev, "pmodclp %s: ERROR: No pmodclp_device structure found!\n", np->name);
- return -ENOSYS;
- }
- pmodclp_dev = (struct pmodclp_device *)(np->data);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Free text buffer.\n", np->name);
-#endif
-
- if (pmodclp_dev->txt_buf != NULL)
- kfree(pmodclp_dev->txt_buf);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Unregister gpio_spi Platform Devices.\n", np->name);
-#endif
-
- np->data = NULL;
- device_num--;
-
- /* Destroy pmodclp class, Release device id Region after
- * all pmodclp devices have been removed.
- */
- if (device_num == 0) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Destroy pmodclp_gpio Class.\n");
-#endif
-
- if (pmodclp_class)
- class_destroy(pmodclp_class);
- pmodclp_class = NULL;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Release Char Device Region.\n");
-#endif
-
- unregister_chrdev_region(pmodclp_dev_id, MAX_PMODCLP_DEV_NUM);
- pmodclp_dev_id = 0;
- }
-
- return 0;
-}
-
-static struct platform_driver pmodclp_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = pmodclp_of_match,
- },
- .probe = pmodclp_of_probe,
- .remove = pmodclp_of_remove,
-};
-
-module_platform_driver(pmodclp_driver);
-
-MODULE_AUTHOR("Digilent, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_NAME ": PmodCLP display driver");
-MODULE_ALIAS(DRIVER_NAME);
+++ /dev/null
-/*
- * pmodcls.c - Digilent PmodCLS driver
- *
- * Copyright (c) 2012 Digilent. All right reserved.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "pmodcls"
-#define SPI_DRIVER_NAME "pmodcls-spi"
-
-#define DEFAULT_SPI_SPEED 625000
-#define MAX_PMODCLS_DEV_NUM 16
-#define TXT_BUF_SIZE 1024
-#define MAX_NO_ROWS 2 /* The device has 2 rows */
-
-static dev_t pmodcls_dev_id;
-static unsigned int device_num;
-static unsigned int cur_minor;
-static unsigned int spi_drv_registered;
-static struct class *pmodcls_class;
-
-struct pmodcls_device {
- char *name;
- /* R/W Mutex Lock */
- struct mutex mutex;
- /* Text Buffer */
- char *txt_buf; /* Device Text buffer */
- int cur_row; /* Maintain current row */
- int exceeded_rows; /* Flag for situation where maximum number of rows is exceeded */
- /* Pin Assignment */
-
- unsigned long iSCLK;
- unsigned long iSDIN;
- unsigned long iCS;
-
- /* SPI Info */
- uint32_t spi_speed;
- uint32_t spi_id;
- /* platform device structures */
- struct platform_device *pdev;
- /* Char Device */
- struct cdev cdev;
- struct spi_device *spi;
- dev_t dev_id;
-};
-
-/* Forward definitions */
-static int parse_text(char *txt_buf, int cnt, struct pmodcls_device *dev);
-static int txt_buf_to_display(char *txt_buf, int cnt, struct pmodcls_device *dev);
-
-/**
- * A basic open function.
- */
-static int pmodcls_open(struct inode *inode, struct file *fp)
-{
- struct pmodcls_device *dev;
-
- dev = container_of(inode->i_cdev, struct pmodcls_device, cdev);
- fp->private_data = dev;
-
- return 0;
-}
-
-/**
- * A basic close function, do nothing.
- */
-static int pmodcls_close(struct inode *inode, struct file *fp)
-{
- return 0;
-}
-
-/*
- * Driver write function
- *
- * This function uses a generic SPI write to send values to the Pmod device.
- * It takes a string from the app in the buffer.
- * It sends the commands and the text to PmodCLS over the standard SPI interface.
- *
- */
-static ssize_t pmodcls_write(struct file *fp, const char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- int status; /* spi_write return value */
- int cnt;
- struct pmodcls_device *dev;
-
- dev = fp->private_data;
-
- if (mutex_lock_interruptible(&dev->mutex)) {
- retval = -ERESTARTSYS;
- goto write_lock_err;
- }
-
- cnt = length;
-
- if (copy_from_user(dev->txt_buf, buffer, cnt)) {
- retval = -EFAULT;
- goto quit_write;
- }
- retval = cnt;
-
- dev->txt_buf[cnt] = '\0';
- status = parse_text(dev->txt_buf, cnt, dev);
- dev_dbg(&dev->spi->dev, "cls_write: Writing \"%s\" to display\n",
- dev->txt_buf);
-
- if (status) {
- dev_err(&dev->spi->dev, "cls_write: Error writing text to SPI device\n");
- retval = -EFAULT;
- goto quit_write;
- }
- dev_dbg(&dev->spi->dev, "cls_write: Writing to display complete\n");
-
-quit_write:
- mutex_unlock(&dev->mutex);
-write_lock_err:
- return retval;
-}
-
-/**
- * parse_text - This function builds the commands to be sent for each recognized escape sequence.
- *
- * Parameters
- * @char *txt_buf: the text array to be parsed
- * @int cnt the number of charcaters to be parsed in the text array
- * @struct pmodclp_device *dev pointer to device structure
- *
- *
- * This function parses a text array, containing a sequence of one or more text or commands to be sent to PmodCLS. Its purpose is:
- * - recognize, interpret the commands:
- * - maintain a shadow value of the current row (this is because PmodCLS is a "write only" device, the cursor position cannot be read)
- * - split the separate commands / text and send individually to the device.
- * - recognize LF character ('\n') inside a text to be sent to the device
- * - if current line is the first, move the cursor to the beginning of the next line
- * - if current line is the second, there is no room for new line. Text characters after LF are ignored, commands are still interpreted.
- *
- */
-static int parse_text(char *txt_buf, int cnt, struct pmodcls_device *dev)
-{
- int status = 0; /* spi_write return value */
- int is_ignore_txt;
- int is_par1 = 0;
- int is_cmd = 0;
- int par1 = 0, par2;
- char txt_LF_cmd[10];
- char *parse_buf, *sent_buf;
-
- parse_buf = txt_buf;
- sent_buf = txt_buf - 1;
- is_par1 = 0;
- is_cmd = 0;
- is_ignore_txt = dev->exceeded_rows;
- while ((!status) && (parse_buf < (txt_buf + cnt))) {
- /* recognize command - look for ESC code, followed by '[' */
- if ((!is_cmd) && ((*parse_buf) == 0x1B) && (parse_buf[1] == '[')) {
- /* enter command mode */
- is_cmd = 1;
- is_par1 = 1;
- par1 = 0;
- /* send previous text (before the ESC sequence) */
- if ((parse_buf - sent_buf) > 1) {
- status = txt_buf_to_display(sent_buf + 1, parse_buf - 1 - sent_buf, dev);
- sent_buf = parse_buf - 1;
- }
- parse_buf++; /* skip '[' char */
-
- } else {
- if (is_cmd) {
- if ((*parse_buf) >= '0' && (*parse_buf) <= '9') { /* numeric char, build parameters values */
- if (is_par1)
- par1 = 10 * par1 + (*parse_buf) - '0';
- else
- par2 = 10 * par2 + (*parse_buf) - '0';
- } else {
- if ((*parse_buf) == ';') { /* parameters separator */
- is_par1 = 0;
- par2 = 0;
- } else {
- /* look for some commands */
- switch (*parse_buf) {
- case 'H': /* set cursor position */
- dev->cur_row = par1;
- if (dev->cur_row < MAX_NO_ROWS)
- is_ignore_txt = 0;
- else
- is_ignore_txt = 1;
- break;
- case 'j': /* clear display and home cursor */
- case '*': /* reset */
- dev->cur_row = 0;
- is_ignore_txt = 0;
- break;
- case 's': /* save cursor position */
- case 'u': /* restore saved cursor position */
- case 'K': /* erase within line */
- case 'N': /* erase field */
- case '@': /* scroll left */
- case 'A': /* scroll right */
- case 'h': /* set display mode (wrap line) */
- case 'c': /* set cursor mode */
- case 'p': /* program char table into LCD */
- case 't': /* save RAM character table to EEPROM */
- case 'l': /* load EEPROM character table to RAM */
- case 'd': /* define user programmable character */
- case 'm': /* save communication mode to EEPROM */
- case 'w': /* enable write to EEPROM */
- case 'n': /* save cursor mode to EEPROM */
- case 'o': /* save display mode to EEPROM */
- /* cursor is not affected */
- break;
-
- default:
- /* no command was recognized */
- is_cmd = 0; /* comand mode is abandonned */
- }
- if (is_cmd) {
- /* send text including the command char */
- if ((parse_buf - sent_buf) > 1) {
- status = txt_buf_to_display(sent_buf + 1, parse_buf - sent_buf, dev);
- sent_buf = parse_buf;
- is_cmd = 0; /* comand mode is abandonned */
- }
- }
- }
- }
- } else {
- /* free text, not inside a command */
- if (is_ignore_txt) {
- sent_buf = parse_buf;
- } else {
- if ((*parse_buf) == '\n') { /* LF */
- /* send text before the LF char */
- if ((parse_buf - sent_buf) > 0)
- txt_buf_to_display(sent_buf + 1, parse_buf - 1 - sent_buf, dev);
- /* position the cursor on the beginning of the next line */
- if (dev->cur_row < (MAX_NO_ROWS - 1)) {
- dev->cur_row++;
- strcpy(txt_LF_cmd, "0[0;0H");
- txt_LF_cmd[0] = 0x1B; /* ESC */
- txt_LF_cmd[2] = '0' + (unsigned char)dev->cur_row;
- status = txt_buf_to_display(txt_LF_cmd, 6, dev);
- } else {
- /* there is no room to place a third line. Ignore text (still look for the comands) */
- is_ignore_txt = 1;
- }
-
- /* advance the pointers so that LF char is skipped next time when chars are sent */
- sent_buf = parse_buf;
- }
-
- }
- }
-
- }
- parse_buf++; /* advance one character */
- }
- parse_buf--;
- /* send remaining chars */
-
- if ((!status) && ((parse_buf - sent_buf) > 0))
- status = txt_buf_to_display(sent_buf + 1, parse_buf - sent_buf, dev);
-
- dev->exceeded_rows = is_ignore_txt;
-
- return status;
-}
-
-/**
- * txt_buf_to_display - This function breaks sends the string to the PmodCLS device over the SPI.
- prior to .
- *
- * Parameters
- * @char *txt_buf: the text array to be parsed
- * @int cnt the number of charcaters to be parsed in the text array
- * @struct pmodclp_device *dev pointer to device structure
- *
- *
- * This function breaks sends the string to the PmodCLS device over the SPI.
- * It breaks the input string in chunks of 3 bytes in order to reduce the load on the receiving
- * PmodCLS.
- *
- */
-static int txt_buf_to_display(char *txt_buf, int cnt, struct pmodcls_device *dev)
-{
- int status; /* spi_write return value */
- int short_cnt;
-
- /*
- * Break writes into three byte chunks so that the microcontroller on
- * the PmodCLS can keep up. Prior to breaking up writes to these
- * smaller chunks, every 4th character would not be displayed.
- *
- * NOTE: The usleep_range delay is not needed, but allows the driver
- * to relinquish control to other tasks.
- */
- status = 0;
- while ((cnt > 0) && (!status)) {
- short_cnt = (cnt > 3) ? 3 : cnt;
- status = spi_write(dev->spi, txt_buf, short_cnt);
- cnt -= short_cnt;
- txt_buf += short_cnt;
- usleep_range(10, 100);
- }
- return status;
-
-}
-
-/**
- * Driver Read Function
- *
- * This function does not actually read the PmodCLP as it is a write-only device.
- */
-static ssize_t pmodcls_read(struct file *fp, char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
-
- return retval;
-}
-
-static const struct file_operations pmodcls_cdev_fops = {
- .owner = THIS_MODULE,
- .write = pmodcls_write,
- .read = pmodcls_read,
- .open = pmodcls_open,
- .release = pmodcls_close,
-};
-
-/**
- * add_pmodcls_device_to_bus - Add device to SPI bus, initialize SPI data.
- * @dev: pointer to device tree node
- *
- * This function adds device to SPI bus, initialize SPI data.
- */
-static int add_pmodcls_device_to_bus(struct pmodcls_device *dev)
-{
- struct spi_master *spi_master;
- struct spi_device *spi_device;
- int status = 0;
-
- spi_master = spi_busnum_to_master(dev->spi_id);
- if (!spi_master) {
- dev_err(&dev->pdev->dev, "spi_busnum_to_master(%d) returned NULL\n", dev->spi_id);
- return -ENOSYS;
- }
-
- spi_device = spi_alloc_device(spi_master);
- if (!spi_device) {
- put_device(&spi_master->dev);
- dev_err(&dev->pdev->dev, "spi_alloc_device() failed\n");
- return -ENOMEM;
- }
-
- spi_device->chip_select = 0;
- spi_device->max_speed_hz = dev->spi_speed;
- spi_device->mode = SPI_MODE_0;
- spi_device->bits_per_word = 8;
- spi_device->controller_data = (void *)dev->iCS;
- spi_device->dev.platform_data = dev;
- strlcpy(spi_device->modalias, SPI_DRIVER_NAME, sizeof(SPI_DRIVER_NAME));
-
- status = spi_add_device(spi_device);
- if (status < 0) {
- spi_dev_put(spi_device);
- dev_err(&dev->pdev->dev, "spi_add_device() failed %d\n", status);
- return status;
- }
- dev->spi = spi_device;
-
- put_device(&spi_master->dev);
- pr_info(DRIVER_NAME " SPI initialized, max_speed_hz\t%d\n", spi_device->max_speed_hz);
-
- return status;
-}
-
-/**
- * pmodcls_setup_cdev - Setup Char Device for ZED PmodCLP device.
- * @dev: pointer to device tree node
- * @dev_id: pointer to device major and minor number
- * @spi: pointer to spi_device structure
- *
- * This function initializes char device for PmodCLS device, and add it into
- * kernel device structure. It returns 0, if the cdev is successfully
- * initialized, or a negative value if there is an error.
- */
-static int pmodcls_setup_cdev(struct pmodcls_device *dev, dev_t *dev_id, struct spi_device *spi)
-{
- int status = 0;
- struct device *device;
-
- cdev_init(&dev->cdev, &pmodcls_cdev_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &pmodcls_cdev_fops;
- dev->spi = spi;
-
- *dev_id = MKDEV(MAJOR(pmodcls_dev_id), cur_minor++);
- status = cdev_add(&dev->cdev, *dev_id, 1);
- if (status < 0)
- return status;
-
- /* Add Device node in system */
- device = device_create(pmodcls_class, NULL,
- *dev_id, NULL,
- "%s", dev->name);
- if (IS_ERR(device)) {
- status = PTR_ERR(device);
- dev_err(&spi->dev, "failed to create device node %s, err %d\n",
- dev->name, status);
- cdev_del(&dev->cdev);
- }
-
- return status;
-}
-
-/**
- * SPI hardware probe. Sets correct SPI mode, attempts
- * to obtain memory needed by the driver, and performs
- * a simple initialization of the device.
- */
-static int pmodcls_spi_probe(struct spi_device *spi)
-{
- int status = 0;
- struct pmodcls_device *pmodcls_dev;
-
- /* We must use SPI_MODE_0 */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
-
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
- spi->mode, spi->max_speed_hz / 1000,
- status);
- goto spi_err;
- }
-
- /* Get pmodcls_device structure */
- pmodcls_dev = (struct pmodcls_device *)spi->dev.platform_data;
- if (pmodcls_dev == NULL) {
- dev_err(&spi->dev, "Cannot get pmodcls_device.\n");
- status = -EINVAL;
- goto spi_platform_data_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", pmodcls_dev->name);
-#endif
-
- /* Setup char driver */
- status = pmodcls_setup_cdev(pmodcls_dev, &(pmodcls_dev->dev_id), spi);
- if (status) {
- pr_info(" spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
- dev_err(&spi->dev, "spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
- goto cdev_add_err;
- }
-
- /* Initialize Mutex */
- mutex_init(&pmodcls_dev->mutex);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", pmodcls_dev->name);
-#endif
-
- return status;
-
-cdev_add_err:
-spi_platform_data_err:
-spi_err:
- return status;
-}
-
-static int pmodcls_spi_remove(struct spi_device *spi)
-{
- int status;
- struct pmodcls_device *dev;
-
- dev = (struct pmodcls_device *)spi->dev.platform_data;
-
- if (dev == NULL) {
- dev_err(&spi->dev, "spi_remove: Error fetch pmodcls_device struct\n");
- return -EINVAL;
- }
-
- if (&dev->cdev) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
-#endif
- device_destroy(pmodcls_class, dev->dev_id);
- cdev_del(&dev->cdev);
- }
-
- cur_minor--;
-
- return status;
-}
-
-static struct spi_driver pmodcls_spi_driver = {
- .driver = {
- .name = SPI_DRIVER_NAME,
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = pmodcls_spi_probe,
- .remove = pmodcls_spi_remove,
-};
-
-static const struct of_device_id pmodcls_of_match[] = {
- { .compatible = "dglnt,pmodcls", },
- {},
-};
-MODULE_DEVICE_TABLE(of, pmodcls_of_match);
-
-/**
- * pmodcls_of_probe - Probe method for PmodCLS device (over GPIO).
- * @pdev: pointer to platform devices
- *
- * This function probes the PmodCLS device in the device tree. It initializes the
- * PmodCLS driver data structure. It returns 0, if the driver is bound to the PmodCLS
- * device, or a negative value if there is an error.
- */
-static int pmodcls_of_probe(struct platform_device *pdev)
-{
- struct pmodcls_device *pmodcls_dev;
- struct platform_device *pmodcls_pdev;
- struct spi_gpio_platform_data *pmodcls_pdata;
-
- struct device_node *np = pdev->dev.of_node;
-
- const u32 *tree_info;
- const u32 *spi_speed;
- int status = 0;
-
- /* Alloc Space for platform device structure */
- pmodcls_dev = kzalloc(sizeof(*pmodcls_dev), GFP_KERNEL);
- if (!pmodcls_dev) {
- status = -ENOMEM;
- goto dev_alloc_err;
- }
-
- /* Alloc Text Buffer for device */
- pmodcls_dev->txt_buf = kmalloc(TXT_BUF_SIZE, GFP_KERNEL);
- if (!pmodcls_dev->txt_buf) {
- status = -ENOMEM;
- dev_err(&pdev->dev, "Device Display data buffer allocation failed: %d\n", status);
- goto txt_buf_alloc_err;
- }
-
- /* Get the GPIO Pins */
-
- pmodcls_dev->iSCLK = of_get_named_gpio(np, "spi-sclk-gpio", 0);
- pmodcls_dev->iSDIN = of_get_named_gpio(np, "spi-sdin-gpio", 0);
- status = of_get_named_gpio(np, "spi-cs-gpio", 0);
- pmodcls_dev->iCS = (status < 0) ? SPI_GPIO_NO_CHIPSELECT : status;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: iSCLK: 0x%lx\n", np->name, pmodcls_dev->iSCLK);
- pr_info(DRIVER_NAME " %s: iSDIN: 0x%lx\n", np->name, pmodcls_dev->iSDIN);
- pr_info(DRIVER_NAME " %s: iCS : 0x%lx\n", np->name, pmodcls_dev->iCS);
-#endif
-
- /* Get SPI Related Params */
- tree_info = of_get_property(np, "spi-bus-num", NULL);
- if (tree_info) {
- pmodcls_dev->spi_id = be32_to_cpup((tree_info));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: BUS_ID\t%x\n", np->name, pmodcls_dev->spi_id);
-#endif
- }
-
- spi_speed = of_get_property(np, "spi-speed-hz", NULL);
- if (spi_speed) {
- pmodcls_dev->spi_speed = be32_to_cpup((spi_speed));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: SPI_SPEED\t%x\n", np->name, pmodcls_dev->spi_speed);
-#endif
- } else {
- pmodcls_dev->spi_speed = DEFAULT_SPI_SPEED;
- }
- /* Alloc Space for platform data structure */
- pmodcls_pdata = kzalloc(sizeof(*pmodcls_pdata), GFP_KERNEL);
- if (!pmodcls_pdata) {
- status = -ENOMEM;
- goto pdata_alloc_err;
- }
-
- /* Fill up Platform Data Structure */
- pmodcls_pdata->sck = pmodcls_dev->iSCLK;
- pmodcls_pdata->miso = SPI_GPIO_NO_MISO;
- pmodcls_pdata->mosi = pmodcls_dev->iSDIN;
- pmodcls_pdata->num_chipselect = 1;
-
- /* Alloc Space for platform data structure */
- pmodcls_pdev = kzalloc(sizeof(*pmodcls_pdev), GFP_KERNEL);
- if (!pmodcls_pdev) {
- status = -ENOMEM;
- goto pdev_alloc_err;
- }
-
- /* Fill up Platform Device Structure */
- pmodcls_pdev->name = "spi_gpio";
- pmodcls_pdev->id = pmodcls_dev->spi_id;
- pmodcls_pdev->dev.platform_data = pmodcls_pdata;
- pmodcls_dev->pdev = pmodcls_pdev;
-
- /* Register spi_gpio master */
- status = platform_device_register(pmodcls_dev->pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "platform_device_register failed: %d\n", status);
- goto pdev_reg_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi_gpio platform device registered.\n", np->name);
-#endif
- pmodcls_dev->name = (char *)np->name;
-
- /* Fill up Board Info for SPI device */
- status = add_pmodcls_device_to_bus(pmodcls_dev);
- if (status < 0) {
- dev_err(&pdev->dev, "add_pmodcls_device_to_bus failed: %d\n", status);
- goto spi_add_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi device registered.\n", np->name);
-#endif
-
- /* Point device node data to pmodcls_device structure */
- if (np->data == NULL)
- np->data = pmodcls_dev;
-
- if (pmodcls_dev_id == 0) {
- /* Alloc Major & Minor number for char device */
- status = alloc_chrdev_region(&pmodcls_dev_id, 0, MAX_PMODCLS_DEV_NUM, DRIVER_NAME);
- if (status) {
- dev_err(&pdev->dev, "Character device region not allocated correctly: %d\n", status);
- goto err_alloc_chrdev_region;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Char Device Region Registered, with Major: %d.\n",
- MAJOR(pmodcls_dev_id));
-#endif
- }
-
- if (pmodcls_class == NULL) {
- /* Create Pmodcls Device Class */
- pmodcls_class = class_create(THIS_MODULE, DRIVER_NAME);
- if (IS_ERR(pmodcls_class)) {
- status = PTR_ERR(pmodcls_class);
- goto err_create_class;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : pmodcls device class registered.\n");
-#endif
- }
-
- if (spi_drv_registered == 0) {
- /* Register SPI Driver for Pmodcls Device */
- status = spi_register_driver(&pmodcls_spi_driver);
- if (status < 0) {
- dev_err(&pdev->dev, "pmodcls_spi_driver register failed: %d\n", status);
- goto err_spi_register;
- }
- spi_drv_registered = 1;
- }
-
- device_num++;
-
- return status;
-
-err_spi_register:
- class_destroy(pmodcls_class);
- pmodcls_class = NULL;
-err_create_class:
- unregister_chrdev_region(pmodcls_dev_id, MAX_PMODCLS_DEV_NUM);
- pmodcls_dev_id = 0;
-err_alloc_chrdev_region:
- spi_unregister_device(pmodcls_dev->spi);
-spi_add_err:
- platform_device_unregister(pmodcls_dev->pdev);
-pdev_reg_err:
- kfree(pmodcls_pdev);
-pdev_alloc_err:
- kfree(pmodcls_pdata);
-pdata_alloc_err:
- kfree(pmodcls_dev->txt_buf);
-txt_buf_alloc_err:
- kfree(pmodcls_dev);
-dev_alloc_err:
- return status;
-}
-
-/**
- * pmodcls_of_remove - Remove method for ZED PmodCLS device.
- * @np: pointer to device tree node
- *
- * This function removes the PmodCLS device in the device tree. It frees the
- * PmodCLS driver data structure. It returns 0, if the driver is successfully
- * removed, or a negative value if there is an error.
- */
-static int pmodcls_of_remove(struct platform_device *pdev)
-{
- struct pmodcls_device *pmodcls_dev;
- struct device_node *np = pdev->dev.of_node;
-
- if (np->data == NULL) {
- dev_err(&pdev->dev, "pmodcls %s: ERROR: No pmodcls_device structure found!\n", np->name);
- return -ENOSYS;
- }
- pmodcls_dev = (struct pmodcls_device *)(np->data);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Free display buffer.\n", np->name);
-#endif
-
- if (pmodcls_dev->txt_buf != NULL)
- kfree(pmodcls_dev->txt_buf);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Unregister gpio_spi Platform Devices.\n", np->name);
-#endif
-
- if (pmodcls_dev->pdev != NULL)
- platform_device_unregister(pmodcls_dev->pdev);
-
- np->data = NULL;
- device_num--;
-
- /* Unregister SPI Driver, Destroy pmodcls class, Release device id Region after
- * all pmodcls devices have been removed.
- */
- if (device_num == 0) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Unregister SPI Driver.\n");
-#endif
- spi_unregister_driver(&pmodcls_spi_driver);
- spi_drv_registered = 0;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Destroy pmodcls_gpio Class.\n");
-#endif
-
- if (pmodcls_class)
- class_destroy(pmodcls_class);
- pmodcls_class = NULL;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Release Char Device Region.\n");
-#endif
-
- unregister_chrdev_region(pmodcls_dev_id, MAX_PMODCLS_DEV_NUM);
- pmodcls_dev_id = 0;
- }
-
- return 0;
-}
-
-static struct platform_driver pmodcls_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = pmodcls_of_match,
- },
- .probe = pmodcls_of_probe,
- .remove = pmodcls_of_remove,
-};
-
-module_platform_driver(pmodcls_driver);
-
-MODULE_AUTHOR("Digilent, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_NAME ": PmodCLS display driver");
-MODULE_ALIAS(DRIVER_NAME);
+++ /dev/null
-/*
- * pmodda1.c - Digilent PmodDA1 driver
- *
- * Copyright (c) 2012 Digilent. All right reserved.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "pmodda1"
-#define SPI_DRIVER_NAME "pmodda1-spi"
-
-#define DEFAULT_SPI_SPEED 625000
-/* only 2 channels as SPI does not allow write on Data In line. */
-#define PMODDA1_DEV_NUM 2
-
-#define DEFAULT_BUF_SZ 512
-/*
- * default size of the buffer for each DAC on the device. Can be
- * changed from the default during insmod.
- */
-static int buf_sz = DEFAULT_BUF_SZ;
-module_param(buf_sz, int, 0);
-
-static dev_t pmodda1_first_dev_id;
-static struct spi_device *spi_device;
-static unsigned int spi_drv_registered;
-static struct class *pmodda1_class;
-
-struct pmodda1_device {
- char *name;
-
- unsigned int minor_id;
- /* Data Buffer */
- unsigned char *buf;
- /* Pin Assignment */
-
- unsigned long iSCLK;
- unsigned long iSDIN;
- unsigned long iCS;
-
- /* SPI Info */
- uint32_t spi_speed;
- uint32_t spi_id;
- /* platform device structures */
- struct platform_device *pdev;
- /* Char Device */
- struct cdev cdev;
- struct spi_device *spi;
- dev_t dev_id;
-};
-
-/*
- * create a shadow register to configure the devices using a
- * struct for the physical D to A parts used on the Pmod.
- * this is intended to hold the state for each D to A such
- * that a control word can be built for the device when a
- * write request comes for any of the channels.
- */
-struct ad7303 {
- bool ext;
- bool ldac;
- bool pdb;
- bool pda;
- bool sel; /* !A/B in the datasheet for the AD7303, bit 10 in the control reg */
- bool cr1;
- bool cr0;
- uint8_t a_val;
- uint8_t b_val;
- struct mutex mutex;
-};
-
-static struct pmodda1_device *rgpmodda1_devices[PMODDA1_DEV_NUM]; /* create pointer array to hold device info */
-
-static struct ad7303 dac1;
-/* Forward definitions */
-static uint16_t make_cmd_from_shadow_regs(struct ad7303 *dac);
-
-/* make_cmd_from_shadow_regs
- * @dac the ad7303 structure who's bits are used.
- * this function places the configuration bits in the proper
- * bit position to form the command word the AD73703 expects
- * to receive.
- */
-static uint16_t make_cmd_from_shadow_regs(struct ad7303 *dac)
-{
- uint16_t cd = 0;
-
- cd = ((dac->ext & 1) << 15);
- cd |= ((dac->ldac & 1) << 13);
- cd |= ((dac->pdb & 1) << 12);
- cd |= ((dac->pda & 1) << 11);
- cd |= ((dac->sel & 1) << 10);
- cd |= ((dac->cr1 & 1) << 9);
- cd |= ((dac->cr0 & 1) << 8);
-
- return cd;
-}
-
-/**
- * write_spi_16 - Write a 16 bit variable to SPI in High - Low order.
- * @spi: pointer to spi_device structure
- * @cmd_data: the 16 bits variable containing data to be written to spi
- *
- * This function writes to spi the 16 bits of data, big endian (first High and then Low bytes), regardless of CPU representation.
- * It returns the spi write return value (0 on success).
- */
-static int write_spi_16(struct spi_device *spi, uint16_t cmd_data)
-{
- int status; /* spi_write return value */
- /* Change endianness of data, if necessary
- * The data must be written to SPI in big endian,
- * whereas some CPU architectures are little endian.
- */
- uint16_t write_cmd_data = cpu_to_be16(cmd_data);
-
- status = spi_write(spi, &write_cmd_data, 2);
- return status;
-}
-/**
- * A basic open function.
- */
-static int pmodda1_open(struct inode *inode, struct file *fp)
-{
- struct pmodda1_device *dev;
-
- dev = container_of(inode->i_cdev, struct pmodda1_device, cdev);
- dev->minor_id = iminor(inode);
- fp->private_data = dev;
-
- return 0;
-}
-
-/**
- * A basic close function, do nothing.
- */
-static int pmodda1_close(struct inode *inode, struct file *fp)
-{
- return 0;
-}
-
-/*
- * Driver write function
- *
- * This function uses a generic SPI write to send values to the Pmod device.
- * It takes a string from the app in the buffer.
- * It sends the commands and the text to PmodDA1 over the standard SPI interface.
- *
- */
-static ssize_t pmodda1_write(struct file *fp, const char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- int status; /* spi_write return value */
- int cnt;
- int i;
- unsigned int minor_id;
- struct mutex *mutex;
- uint16_t cmd_data;
- struct pmodda1_device *dev;
-
- dev = fp->private_data;
- minor_id = dev->minor_id;
-
- if (minor_id > PMODDA1_DEV_NUM - 1) {
- dev_err(&dev->spi->dev, "da1_write: ERROR: Attempt to read a non-existant device: %d\n", minor_id);
- retval = -ENOTTY;
- goto bad_device;
- }
- dev = rgpmodda1_devices[minor_id];
- mutex = &dac1.mutex; /* get the mutex for the part we will be programming */
-
- if (mutex_lock_interruptible(mutex)) {
- retval = -ERESTARTSYS;
- goto write_lock_err;
- }
-
- if (length > buf_sz)
- cnt = buf_sz;
- else
- cnt = length;
-
- if (copy_from_user(dev->buf, buffer, cnt)) {
- retval = -EFAULT;
- goto quit_write;
- }
- retval = cnt;
-
- dev->buf[cnt] = '\0';
- /* use the minor id number to select which channel to program */
-
- /* the command word is constructed here */
- if ((minor_id == 0)) {
- dev_dbg(&dev->spi->dev, "da1_write: setting DAC_A (or even number DAC)\n");
- dac1.pda = 0; /* want DAC A powered up, don't touch DAC B's setting */
- dac1.sel = 0; /* this will indicate to load DAC A */
- } else {
- dev_dbg(&dev->spi->dev, "da1_write: setting DAC_B (or odd number DAC)\n");
- dac1.pdb = 0; /* want DAC B powered up, don't touch DAC A's setting */
- dac1.sel = 1; /* this will indicate to load DAC B */
- }
- dac1.ext = 0; /* select internal reference for now */
- dac1.ldac = 1; /* will program DAC input reg from shift reg and update both DAC registers */
-
- cmd_data = make_cmd_from_shadow_regs(&dac1);
- for (i = 0; i < cnt; i++) {
- cmd_data &= 0xFF00;
- cmd_data |= dev->buf[i];
-
- status = write_spi_16(dev->spi, cmd_data);
-
- if (!status) /* seems like spi_write returns 0 on success */
- retval = i + 1; /* but system write function expects to see number of bytes written */
- else
- retval = -EIO;
- }
-
- /* save the last value written to the DAC */
- switch (minor_id) {
- case 0:
- dac1.a_val = dev->buf[i - 1];
- break;
- case 1:
- dac1.b_val = dev->buf[i - 1];
- break;
- }
-quit_write:
- mutex_unlock(mutex);
- dev_dbg(&dev->spi->dev, "da1_write: Writing to display complete\n");
-bad_device:
-write_lock_err:
-
- return retval;
-}
-
-/*
- * Driver read function
- *
- * This function does not actually read the Pmod as it is a read-only device. Instead
- * it returns a shadowed copy of the value that was used when the DAC was last programmed.
- */
-static ssize_t pmodda1_read(struct file *fp, char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- int i;
- struct pmodda1_device *dev;
- unsigned int minor_id;
- uint8_t rd_val;
- struct mutex *mutex;
- int cnt;
-
- dev = fp->private_data;
- minor_id = dev->minor_id;
-
- if (length > buf_sz)
- cnt = buf_sz;
- else
- cnt = length;
- if (minor_id > PMODDA1_DEV_NUM - 1) {
- dev_err(&dev->spi->dev, "da1_read: ERROR: Attempt to read a non-existant device: %d\n", minor_id);
- retval = -ENOTTY;
- goto bad_device;
- }
-
- if (minor_id < 2)
- mutex = &dac1.mutex;
-
- if (mutex_lock_interruptible(mutex)) {
- retval = -ERESTARTSYS;
- goto lock_err;
- }
-
- if (buffer == NULL) {
- dev_err(&dev->spi->dev, "da1_read: ERROR: invalid buffer address: 0x%08lx\n", (__force unsigned long)buffer);
- retval = -EINVAL;
- goto quit_read;
- }
-
- /* ok, can use the minor id number to select which DAC value to return */
- switch (minor_id) {
- case 0:
- rd_val = dac1.a_val;
- break;
- case 1:
- rd_val = dac1.b_val;
- break;
- default:
- rd_val = 0; /* git rid of warning about rd_val may be used uninitialized */
- }
-/* tmp, read value from spi */
- spi_read(dev->spi, &rd_val, 2);
- pr_info(DRIVER_NAME "Read values Last Value\t%X\n", rd_val);
- for (i = 0; i < cnt; i++)
- dev->buf[i] = rd_val;
-
- retval = copy_to_user(buffer, (void *)dev->buf, cnt);
- if (!retval)
- retval = cnt; /* copy success, return amount in buffer */
-
-quit_read:
- mutex_unlock(mutex);
-lock_err:
-bad_device:
- return retval;
-}
-
-static const struct file_operations pmodda1_cdev_fops = {
- .owner = THIS_MODULE,
- .write = pmodda1_write,
- .read = pmodda1_read,
- .open = pmodda1_open,
- .release = pmodda1_close,
-};
-
-/**
- * add_pmodda1_device_to_bus - Add device to SPI bus, initialize SPI data.
- * @dev: pointer to device tree node
- *
- * This function adds device to SPI bus, initialize SPI data.
- */
-static int add_pmodda1_device_to_bus(struct pmodda1_device *dev)
-{
- struct spi_master *spi_master;
- int status = 0;
-
- spi_master = spi_busnum_to_master(dev->spi_id);
- if (!spi_master) {
- dev_err(&dev->pdev->dev, "spi_busnum_to_master(%d) returned NULL\n", dev->spi_id);
- return -ENOSYS;
- }
-
- spi_device = spi_alloc_device(spi_master);
- if (!spi_device) {
- put_device(&spi_master->dev);
- dev_err(&dev->pdev->dev, "spi_alloc_device() failed\n");
- return -ENOMEM;
- }
-
- spi_device->chip_select = 0;
- spi_device->max_speed_hz = dev->spi_speed;
- spi_device->mode = SPI_MODE_0;
- spi_device->bits_per_word = 8;
- spi_device->controller_data = (void *)dev->iCS;
- spi_device->dev.platform_data = dev;
- strlcpy(spi_device->modalias, SPI_DRIVER_NAME, sizeof(SPI_DRIVER_NAME));
-
- status = spi_add_device(spi_device);
- if (status < 0) {
- spi_dev_put(spi_device);
- dev_err(&dev->pdev->dev, "spi_add_device() failed %d\n", status);
- return status;
- }
- dev->spi = spi_device;
-
- put_device(&spi_master->dev);
- pr_info(DRIVER_NAME " SPI initialized, max_speed_hz\t%d\n", spi_device->max_speed_hz);
-
- return status;
-}
-
-/**
- * pmodda1_setup_cdev - Setup Char Device for ZED PmodDA1 device.
- * @dev: pointer to device tree node
- * @dev_id: pointer to device major and minor number
- * @spi: pointer to spi_device structure
- *
- * This function initializes char device for PmodDA1 device, and add it into
- * kernel device structure. It returns 0, if the cdev is successfully
- * initialized, or a negative value if there is an error.
- */
-static int pmodda1_setup_cdev(struct pmodda1_device *dev, dev_t *dev_id, int idx, struct spi_device *spi)
-{
- int status = 0;
- struct device *device;
- unsigned int major_id = MAJOR(pmodda1_first_dev_id);
- unsigned int minor_id = MINOR(pmodda1_first_dev_id) + idx;
- char min_name[50];
-
- cdev_init(&dev->cdev, &pmodda1_cdev_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &pmodda1_cdev_fops;
- dev->spi = spi;
-
- *dev_id = MKDEV(major_id, minor_id);
- status = cdev_add(&dev->cdev, *dev_id, 1);
- if (status < 0)
- return status;
-
- /* Add Device node in system */
- sprintf(min_name, "%s_%d", dev->name, idx);
- device = device_create(pmodda1_class, NULL,
- *dev_id, NULL,
- min_name);
- if (IS_ERR(device)) {
- status = PTR_ERR(device);
- dev_err(&spi->dev, "failed to create device node %s, err %d\n",
- dev->name, status);
- cdev_del(&dev->cdev);
- }
- pr_info(SPI_DRIVER_NAME "pmodda1_setup_cdev: Create device %s, major %d, minor %d\n",
- min_name, major_id, minor_id);
- return status;
-}
-
-/**
- * SPI hardware probe. Sets correct SPI mode, attempts
- * to obtain memory needed by the driver and, for each
- * desired minor number device, it performs a simple
- * initialization of the corresponding device.
- */
-static int pmodda1_spi_probe(struct spi_device *spi)
-{
- int status = 0;
- struct pmodda1_device *pmodda1_dev;
- int i;
-
- /* We must use SPI_MODE_0 */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
-
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
- spi->mode, spi->max_speed_hz / 1000,
- status);
- goto spi_err;
- }
-
- /* Get pmodda1_device structure */
- pmodda1_dev = (struct pmodda1_device *)spi->dev.platform_data;
- if (pmodda1_dev == NULL) {
- dev_err(&spi->dev, "Cannot get pmodda1_device.\n");
- status = -EINVAL;
- goto spi_platform_data_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", pmodda1_dev->name);
-#endif
- for (i = 0; i < PMODDA1_DEV_NUM; i++) {
- rgpmodda1_devices[i] = kmalloc(sizeof(struct pmodda1_device), GFP_KERNEL);
- if (!rgpmodda1_devices[i]) {
- status = -ENOMEM;
- dev_err(&spi->dev, "da1_spi_probe: Device structure allocation failed: %d for device %d\n", status, i);
- goto dev_alloc_err;
- }
- rgpmodda1_devices[i]->buf = NULL;
- }
-
- for (i = 0; i < PMODDA1_DEV_NUM; i++) {
- rgpmodda1_devices[i]->buf = kmalloc(buf_sz, GFP_KERNEL);
- if (!rgpmodda1_devices[i]->buf) {
- status = -ENOMEM;
- dev_err(&spi->dev, "Device value buffer allocation failed: %d\n", status);
- goto buf_alloc_err;
- }
- rgpmodda1_devices[i]->spi = spi_device;
- }
-
- /* Setup char driver for each device*/
- for (i = 0; i < PMODDA1_DEV_NUM; i++) {
- status = pmodda1_setup_cdev(pmodda1_dev, &(pmodda1_dev->dev_id), i, spi);
- if (status) {
- dev_err(&spi->dev, "pmodda1_spi_probe: Error adding da1_spi device: %d for device %d\n", status, i);
- goto cdev_add_err;
- }
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", pmodda1_dev->name);
-#endif
-
- return status;
-buf_alloc_err:
- for (i = 0; i < PMODDA1_DEV_NUM; i++)
- kfree(rgpmodda1_devices[i]->buf);
-dev_alloc_err:
- for (i = 0; i < PMODDA1_DEV_NUM; i++)
- kfree(rgpmodda1_devices[i]);
-cdev_add_err:
-spi_platform_data_err:
-spi_err:
- return status;
-}
-
-/**
- * pmodda1_spi_remove - SPI hardware remove.
- * Performs tasks required when SPI is removed.
- */
-static int pmodda1_spi_remove(struct spi_device *spi)
-{
- int status;
- struct pmodda1_device *dev;
-
- dev = (struct pmodda1_device *)spi->dev.platform_data;
-
- if (dev == NULL) {
- dev_err(&spi->dev, "spi_remove: Error fetch pmodda1_device struct\n");
- return -EINVAL;
- }
-
- if (&dev->cdev) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
-#endif
- device_destroy(pmodda1_class, dev->dev_id);
- cdev_del(&dev->cdev);
- }
-
- return status;
-}
-
-static struct spi_driver pmodda1_spi_driver = {
- .driver = {
- .name = SPI_DRIVER_NAME,
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = pmodda1_spi_probe,
- .remove = pmodda1_spi_remove,
-};
-
-static const struct of_device_id pmodda1_of_match[] = {
- { .compatible = "dglnt,pmodda1", },
- {},
-};
-MODULE_DEVICE_TABLE(of, pmodda1_of_match);
-
-/**
- * pmodda1_of_probe - Probe method for PmodDA1 device (over GPIO).
- * @pdev: pointer to platform devices
- *
- * This function probes the PmodDA1 device in the device tree. It initializes the
- * PmodDA1 driver data structure. It returns 0, if the driver is bound to the PmodDA1
- * device, or a negative value if there is an error.
- */
-static int pmodda1_of_probe(struct platform_device *pdev)
-{
- struct pmodda1_device *pmodda1_dev;
- struct platform_device *pmodda1_pdev;
- struct spi_gpio_platform_data *pmodda1_pdata;
-
- struct device_node *np = pdev->dev.of_node;
-
- const u32 *tree_info;
- const u32 *spi_speed;
- int status = 0;
- uint16_t cmd_data;
-
- /* Alloc Space for platform device structure */
- pmodda1_dev = kzalloc(sizeof(*pmodda1_dev), GFP_KERNEL);
- if (!pmodda1_dev) {
- status = -ENOMEM;
- goto dev_alloc_err;
- }
-
- pmodda1_dev->buf = kmalloc(buf_sz, GFP_KERNEL);
- if (!pmodda1_dev->buf) {
- status = -ENOMEM;
- pr_info(DRIVER_NAME "Device value buffer allocation failed: %d\n", status);
- goto buf_alloc_err;
- }
-
- /* Get the GPIO Pins */
-
- pmodda1_dev->iSCLK = of_get_named_gpio(np, "spi-sclk-gpio", 0);
- pmodda1_dev->iSDIN = of_get_named_gpio(np, "spi-sdin-gpio", 0);
- status = of_get_named_gpio(np, "spi-cs-gpio", 0);
- pmodda1_dev->iCS = (status < 0) ? SPI_GPIO_NO_CHIPSELECT : status;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: iSCLK: 0x%lx\n", np->name, pmodda1_dev->iSCLK);
- pr_info(DRIVER_NAME " %s: iSDIN: 0x%lx\n", np->name, pmodda1_dev->iSDIN);
- pr_info(DRIVER_NAME " %s: iCS : 0x%lx\n", np->name, pmodda1_dev->iCS);
-#endif
-
- /* Get SPI Related Params */
- tree_info = of_get_property(np, "spi-bus-num", NULL);
- if (tree_info) {
- pmodda1_dev->spi_id = be32_to_cpup((tree_info));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: BUS_ID\t%x\n", np->name, pmodda1_dev->spi_id);
-#endif
- }
-
- spi_speed = of_get_property(np, "spi-speed-hz", NULL);
- if (spi_speed) {
- pmodda1_dev->spi_speed = be32_to_cpup((spi_speed));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: SPI_SPEED\t%x\n", np->name, pmodda1_dev->spi_speed);
-#endif
- } else {
- pmodda1_dev->spi_speed = DEFAULT_SPI_SPEED;
- }
- /* Alloc Space for platform data structure */
- pmodda1_pdata = kzalloc(sizeof(*pmodda1_pdata), GFP_KERNEL);
- if (!pmodda1_pdata) {
- status = -ENOMEM;
- goto pdata_alloc_err;
- }
-
- /* Fill up Platform Data Structure */
- pmodda1_pdata->sck = pmodda1_dev->iSCLK;
- pmodda1_pdata->miso = SPI_GPIO_NO_MISO;
- pmodda1_pdata->mosi = pmodda1_dev->iSDIN;
- pmodda1_pdata->num_chipselect = 1;
-
- /* Alloc Space for platform data structure */
- pmodda1_pdev = kzalloc(sizeof(*pmodda1_pdev), GFP_KERNEL);
- if (!pmodda1_pdev) {
- status = -ENOMEM;
- goto pdev_alloc_err;
- }
-
- /* Fill up Platform Device Structure */
- pmodda1_pdev->name = "spi_gpio";
- pmodda1_pdev->id = pmodda1_dev->spi_id;
- pmodda1_pdev->dev.platform_data = pmodda1_pdata;
- pmodda1_dev->pdev = pmodda1_pdev;
-
- /* Register spi_gpio master */
- status = platform_device_register(pmodda1_dev->pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "platform_device_register failed: %d\n", status);
- goto pdev_reg_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi_gpio platform device registered.\n", np->name);
-#endif
- pmodda1_dev->name = (char *)np->name;
-
- if (pmodda1_first_dev_id == 0) {
- /* Alloc Major & Minor number for char device */
- status = alloc_chrdev_region(&pmodda1_first_dev_id, 0, PMODDA1_DEV_NUM, DRIVER_NAME);
- if (status) {
- dev_err(&pdev->dev, "Character device region not allocated correctly: %d\n", status);
- goto err_alloc_chrdev_region;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Char Device Region Registered, with Major: %d.\n",
- MAJOR(pmodda1_first_dev_id));
-#endif
- }
-
- /* Point device node data to pmodda1_device structure */
- if (np->data == NULL)
- np->data = pmodda1_dev;
-
- if (pmodda1_class == NULL) {
- /* Create Pmodda1 Device Class */
- pmodda1_class = class_create(THIS_MODULE, DRIVER_NAME);
- if (IS_ERR(pmodda1_class)) {
- status = PTR_ERR(pmodda1_class);
- goto err_create_class;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : pmodda1 device class registered.\n");
-#endif
- }
-
- /* Fill up Board Info for SPI device */
- status = add_pmodda1_device_to_bus(pmodda1_dev);
- if (status < 0) {
- dev_err(&pdev->dev, "add_pmodda1_device_to_bus failed: %d\n", status);
- goto spi_add_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi device registered.\n", np->name);
-#endif
-
- if (spi_drv_registered == 0) {
- /* Register SPI Driver for Pmodda1 Device */
- status = spi_register_driver(&pmodda1_spi_driver);
- if (status < 0) {
- dev_err(&pdev->dev, "pmodda1_spi_driver register failed: %d\n", status);
- goto err_spi_register;
- }
- spi_drv_registered = 1;
- }
-
- /*
- * although a well-designed part will power-up into a known good state, this is
- * a good time to force it into a known good state just to be sure. In this case,
- * the desired known good state is both DACs powered down.
- */
- dac1.ext = 0; /* select internal reference for now */
- dac1.ldac = 0; /* want to be able to load both DACs together */
- dac1.pda = 1; /* want DAC A powered down */
- dac1.pdb = 1; /* want DAC B powered down */
- dac1.sel = 0; /* this won't matter this time since both devices will be loaded from the shift register */
- dac1.cr0 = 0; /* in conjunction with cr1 will load both devices from the shift register */
- dac1.cr1 = 0; /* in conjunction with cr0 will load both devices from the shift register */
- mutex_init(&dac1.mutex);
- cmd_data = make_cmd_from_shadow_regs(&dac1);
- /* cmd_data &= 0xFFFF0000; *//* zeroes out the low order bits so that the DAC could be powered up and */
- /* the output would still be zero. */
- status = write_spi_16(rgpmodda1_devices[0]->spi, cmd_data);
- if (status) {
- dev_err(&pdev->dev, "da1_spi_probe: Error writing to device to initally power down: %d\n", status);
- goto initial_state_err;
- }
-
- return status;
-initial_state_err:
-err_spi_register:
- class_destroy(pmodda1_class);
- pmodda1_class = NULL;
-err_create_class:
- unregister_chrdev_region(pmodda1_first_dev_id, PMODDA1_DEV_NUM);
- pmodda1_first_dev_id = 0;
-err_alloc_chrdev_region:
- spi_unregister_device(pmodda1_dev->spi);
-spi_add_err:
- platform_device_unregister(pmodda1_dev->pdev);
-pdev_reg_err:
- kfree(pmodda1_pdev);
-pdev_alloc_err:
- kfree(pmodda1_pdata);
-buf_alloc_err:
-pdata_alloc_err:
- kfree(pmodda1_dev->buf);
- kfree(pmodda1_dev);
-dev_alloc_err:
- return status;
-}
-
-/**
- * pmodda1_of_remove - Remove method for ZED PmodDA1 device.
- * @np: pointer to device tree node
- *
- * This function removes the PmodDA1 device in the device tree. It frees the
- * PmodDA1 driver data structure. It returns 0, if the driver is successfully
- * removed, or a negative value if there is an error.
- */
-static int pmodda1_of_remove(struct platform_device *pdev)
-{
- struct pmodda1_device *pmodda1_dev;
- struct device_node *np = pdev->dev.of_node;
-
- if (np->data == NULL) {
- dev_err(&pdev->dev, "pmodda1 %s: ERROR: No pmodda1_device structure found!\n", np->name);
- return -ENOSYS;
- }
- pmodda1_dev = (struct pmodda1_device *)(np->data);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Free display buffer.\n", np->name);
-#endif
-
- if (pmodda1_dev->buf != NULL)
- kfree(pmodda1_dev->buf);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Unregister gpio_spi Platform Devices.\n", np->name);
-#endif
-
- if (pmodda1_dev->pdev != NULL)
- platform_device_unregister(pmodda1_dev->pdev);
-
- np->data = NULL;
-
- /* Unregister SPI Driver, Destroy pmodda1 class, Release device id Region
- */
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Unregister SPI Driver.\n");
-#endif
- spi_unregister_driver(&pmodda1_spi_driver);
- spi_drv_registered = 0;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Destroy pmodda1_gpio Class.\n");
-#endif
-
- if (pmodda1_class)
- class_destroy(pmodda1_class);
- pmodda1_class = NULL;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Release Char Device Region.\n");
-#endif
-
- unregister_chrdev_region(pmodda1_first_dev_id, PMODDA1_DEV_NUM);
- pmodda1_first_dev_id = 0;
-
- return 0;
-}
-
-static struct platform_driver pmodda1_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = pmodda1_of_match,
- },
- .probe = pmodda1_of_probe,
- .remove = pmodda1_of_remove,
-};
-
-module_platform_driver(pmodda1_driver);
-
-MODULE_AUTHOR("Digilent, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_NAME ": PmodDA1 display driver");
-MODULE_ALIAS(DRIVER_NAME);
+++ /dev/null
-/*
- * pmodolde-gpio.c - PmodOLED-GPIO driver
- *
- * Copyright (c) 2012 Digilent. All right reserved.
- *
- * 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.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <asm/uaccess.h>
-
-#define DRIVER_NAME "pmodoled-gpio"
-#define SPI_DRIVER_NAME "pmodoled-gpio-spi"
-#define MAX_PMODOLED_GPIO_DEV_NUM 16
-#define DISPLAY_BUF_SZ 512 /* 32 x 128 bit monochrome == 512 bytes */
-#define MAX_LINE_LEN 16 /* 128 bits wide and current char width is 8 bit */
-#define MAX_ROW 4
-#define OLED_MAX_PG_CNT 4 /* number of display pages in OLED controller */
-#define OLED_CONTROLLER_PG_SZ 128
-#define OLED_CONTROLLER_CMD 0
-#define OLED_CONTROLLER_DATA 1
-
-/* commands for the OLED display controller */
-#define OLED_SET_PG_ADDR 0x22
-#define OLED_DISPLAY_OFF 0xAE
-#define OLED_DISPLAY_ON 0xAF
-#define OLED_CONTRAST_CTRL 0x81
-#define OLED_SET_PRECHARGE_PERIOD 0xD9
-#define OLED_SET_SEGMENT_REMAP 0xA1
-#define OLED_SET_COM_DIR 0xC8
-#define OLED_SET_COM_PINS 0xDA
-
-static dev_t gpio_pmodoled_dev_id;
-static unsigned int device_num;
-static unsigned int cur_minor;
-static unsigned int spi_drv_registered;
-/* struct mutex minor_mutex; */
-static struct class *gpio_pmodoled_class;
-
-struct gpio_pmodoled_device {
- const char *name;
- /* R/W Mutex Lock */
- struct mutex mutex;
- /* Display Buffers */
- uint8_t disp_on;
- uint8_t *disp_buf;
- /* Pin Assignment */
- unsigned long iVBAT;
- unsigned long iVDD;
- unsigned long iRES;
- unsigned long iDC;
- unsigned long iSCLK;
- unsigned long iSDIN;
- unsigned long iCS;
- /* SPI Info */
- uint32_t spi_id;
- /* platform device structures */
- struct platform_device *pdev;
- /* Char Device */
- struct cdev cdev;
- struct spi_device *spi;
- dev_t dev_id;
-};
-
-/**
- * screen_buf_to_display -
- * @screen_buf -
- * @dev -
- *
- */
-static int screen_buf_to_display(uint8_t *screen_buf, struct gpio_pmodoled_device *dev)
-{
- uint32_t pg;
- int status;
- uint8_t lower_start_column = 0x00;
- uint8_t upper_start_column = 0x10;
- uint8_t wr_buf[10];
-
- for (pg = 0; pg < OLED_MAX_PG_CNT; pg++) {
- wr_buf[0] = OLED_SET_PG_ADDR;
- wr_buf[1] = pg;
- wr_buf[2] = lower_start_column;
- wr_buf[3] = upper_start_column;
- gpio_set_value(dev->iDC, OLED_CONTROLLER_CMD);
- status = spi_write(dev->spi, wr_buf, 4);
- if (status) {
- dev_err(&dev->spi->dev, "screen_buf_to_display: Error writing to SPI\n");
- break;
- }
-
- gpio_set_value(dev->iDC, OLED_CONTROLLER_DATA);
- status = spi_write(dev->spi, (uint8_t *)(screen_buf +
- (pg * OLED_CONTROLLER_PG_SZ)), OLED_CONTROLLER_PG_SZ);
- if (status) {
- dev_err(&dev->spi->dev, "screen_buf_to_display: Error writing to SPI\n");
- break;
- }
- }
- return status;
-}
-
-/**
- * A basic open function. It exists mainly to save the id of
- * the OLED and some other basic information.
- */
-static int gpio_pmodoled_open(struct inode *inode, struct file *fp)
-{
- struct gpio_pmodoled_device *dev;
-
- dev = container_of(inode->i_cdev, struct gpio_pmodoled_device, cdev);
- fp->private_data = dev;
-
- return 0;
-}
-
-static int gpio_pmodoled_close(struct inode *inode, struct file *fp)
-{
- return 0;
-}
-
-/**
- * Driver write function
- *
- * This function uses a generic SPI write to send values to the Pmod device
- * It takes a raw data array from the app in the buffer, copied it into
- * device dispay buffer, and finally sends the buffer to the OLED using SPI
- */
-static ssize_t gpio_pmodoled_write(struct file *fp, const char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- struct gpio_pmodoled_device *dev;
- unsigned int minor_id;
- int cnt;
- int status;
-
- dev = fp->private_data;
- minor_id = MINOR(dev->dev_id);
-
- if (mutex_lock_interruptible(&dev->mutex)) {
- retval = -ERESTARTSYS;
- goto write_lock_err;
- }
-
- if (buffer == NULL) {
- dev_err(&dev->spi->dev, "oled_write: ERROR: invalid buffer address: 0x%08lx\n",
- (__force unsigned long)buffer);
- retval = -EINVAL;
- goto quit_write;
- }
-
- if (length > DISPLAY_BUF_SZ)
- cnt = DISPLAY_BUF_SZ;
- else
- cnt = length;
-
- if (copy_from_user(dev->disp_buf, buffer, cnt)) {
- dev_err(&dev->spi->dev, "oled_write: copy_from_user failed\n");
- retval = -EFAULT;
- goto quit_write;
- } else
- retval = cnt;
-
- status = screen_buf_to_display(dev->disp_buf, dev);
- if (status) {
- dev_err(&dev->spi->dev, "oled_write: Error sending string to display\n");
- retval = -EFAULT;
- goto quit_write;
- }
-
-quit_write:
- mutex_unlock(&dev->mutex);
-write_lock_err:
- return retval;
-}
-
-/**
- * Driver Read Function
- *
- * This function does not actually read the Pmod as it is a write-only device. Instead
- * It returns data in the buffer generated for the display that was used when the OLED
- * was last programmed.
- */
-static ssize_t gpio_pmodoled_read(struct file *fp, char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t retval = 0;
- struct gpio_pmodoled_device *dev;
- unsigned int minor_id;
- int cnt;
-
- dev = fp->private_data;
- minor_id = MINOR(dev->dev_id);
-
- if (mutex_lock_interruptible(&dev->mutex)) {
- retval = -ERESTARTSYS;
- goto read_lock_err;
- }
-
- if (buffer == NULL) {
- dev_err(&dev->spi->dev, "OLED_read: ERROR: invalid buffer "
- "address: 0x%08lx\n", (__force unsigned long)buffer);
- retval = -EINVAL;
- goto quit_read;
- }
-
- if (length > DISPLAY_BUF_SZ)
- cnt = DISPLAY_BUF_SZ;
- else
- cnt = length;
- retval = copy_to_user((void __user *)buffer, dev->disp_buf, cnt);
- if (!retval)
- retval = cnt; /* copy success, return amount in buffer */
-
-quit_read:
- mutex_unlock(&dev->mutex);
-read_lock_err:
- return retval;
-}
-
-static struct file_operations gpio_pmodoled_cdev_fops = {
- .owner = THIS_MODULE,
- .write = gpio_pmodoled_write,
- .read = gpio_pmodoled_read,
- .open = gpio_pmodoled_open,
- .release = gpio_pmodoled_close,
-};
-
-static int add_gpio_pmodoled_device_to_bus(struct gpio_pmodoled_device *dev)
-{
- struct spi_master *spi_master;
- struct spi_device *spi_device;
- int status = 0;
-
- spi_master = spi_busnum_to_master(dev->spi_id);
- if (!spi_master) {
- dev_err(&dev->pdev->dev, "spi_busnum_to_master(%d) returned NULL\n", dev->spi_id);
- return -ENOSYS;
- }
-
- spi_device = spi_alloc_device(spi_master);
- if (!spi_device) {
- put_device(&spi_master->dev);
- dev_err(&dev->pdev->dev, "spi_alloc_device() failed\n");
- return -ENOMEM;
- }
-
- spi_device->chip_select = 0;
- spi_device->max_speed_hz = 4000000;
- spi_device->mode = SPI_MODE_0;
- spi_device->bits_per_word = 8;
- spi_device->controller_data = (void *)dev->iCS;
- spi_device->dev.platform_data = dev;
- strlcpy(spi_device->modalias, SPI_DRIVER_NAME, sizeof(SPI_DRIVER_NAME));
-
- status = spi_add_device(spi_device);
- if (status < 0) {
- spi_dev_put(spi_device);
- dev_err(&dev->pdev->dev, "spi_add_device() failed %d\n", status);
- return status;
- }
- dev->spi = spi_device;
-
- put_device(&spi_master->dev);
-
- return status;
-}
-
-/**
- * gpio_pmodoled_setup_cdev - Setup Char Device for ZED on-board OLED device.
- * @dev: pointer to device tree node
- * @dev_id: pointer to device major and minor number
- * @spi: pointer to spi_device structure
- *
- * This function initializes char device for OLED device, and add it into
- * kernel device structure. It returns 0, if the cdev is successfully
- * initialized, or a negative value if there is an error.
- */
-static int gpio_pmodoled_setup_cdev(struct gpio_pmodoled_device *dev, dev_t *dev_id, struct spi_device *spi)
-{
- int status = 0;
- struct device *device;
-
- cdev_init(&dev->cdev, &gpio_pmodoled_cdev_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &gpio_pmodoled_cdev_fops;
- dev->spi = spi;
-
- *dev_id = MKDEV(MAJOR(gpio_pmodoled_dev_id), cur_minor++);
- status = cdev_add(&dev->cdev, *dev_id, 1);
- if (status < 0)
- return status;
-
- /* Add Device node in system */
- device = device_create(gpio_pmodoled_class, NULL,
- *dev_id, NULL,
- "%s", dev->name);
- if (IS_ERR(device)) {
- status = PTR_ERR(device);
- dev_err(&spi->dev, "failed to create device node %s, err %d\n",
- dev->name, status);
- cdev_del(&dev->cdev);
- }
-
- return status;
-}
-
-/**
- * gpio_pmodoled_init_gpio - Initialize GPIO for ZED Onboard OLED
- * @dev - gpio_pmodoled_device
- *
- * Initializes OLED GPIO Control Pins.
- * It returns 0, if the gpio pins are successfully
- * initialized, or a negative value if there is an error.
- */
-static int gpio_pmodoled_init_gpio(struct gpio_pmodoled_device *dev)
-{
- struct gpio gpio_pmodoled_ctrl[] = {
- { dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat" },
- { dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD" },
- { dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET" },
- { dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C" },
- };
- int status;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(gpio_pmodoled_ctrl); i++) {
- status = gpio_is_valid(gpio_pmodoled_ctrl[i].gpio);
- if (!status) {
- dev_err(&dev->spi->dev, "!! gpio_is_valid for GPIO %d, %s FAILED!, status: %d\n",
- gpio_pmodoled_ctrl[i].gpio, gpio_pmodoled_ctrl[i].label, status);
- goto gpio_invalid;
- }
- }
-
- status = gpio_request_array(gpio_pmodoled_ctrl, ARRAY_SIZE(gpio_pmodoled_ctrl));
- if (status) {
- dev_err(&dev->spi->dev, "!! gpio_request_array FAILED!\n");
- dev_err(&dev->spi->dev, " status is: %d\n", status);
- gpio_free_array(gpio_pmodoled_ctrl, 4);
- goto gpio_invalid;
- }
-
-gpio_invalid:
- return status;
-}
-
-/**
- * gpio_pmodoled_disp_init -
- * @dev:
- *
- */
-static void gpio_pmodoled_disp_init(struct gpio_pmodoled_device *dev)
-{
- int status;
- uint8_t wr_buf[20];
-
- /* We are going to be sending commands
- * so clear the data/cmd bit */
- gpio_set_value(dev->iDC, OLED_CONTROLLER_CMD);
-
- /* Start by turning VDD on and wait for the power to come up */
- gpio_set_value(dev->iVDD, 0);
- msleep(1);
-
- /* Display off Command */
- wr_buf[0] = OLED_DISPLAY_OFF;
- status = spi_write(dev->spi, wr_buf, 1);
-
- /* Bring Reset Low and then High */
- gpio_set_value(dev->iRES, 1);
- msleep(1);
- gpio_set_value(dev->iRES, 0);
- msleep(1);
- gpio_set_value(dev->iRES, 1);
-
- /* Send the set charge pump and set precharge period commands */
- wr_buf[0] = 0x8D;
- wr_buf[1] = 0x14;
- wr_buf[2] = OLED_SET_PRECHARGE_PERIOD;
- wr_buf[3] = 0xF1;
-
- status = spi_write(dev->spi, wr_buf, 4);
-
- /* Turn on VCC and wait 100ms */
- gpio_set_value(dev->iVBAT, 0);
- msleep(100);
-
- /* Set Display COntrast */
- wr_buf[0] = OLED_CONTRAST_CTRL;
- wr_buf[1] = 0x0F;
-
- /* Invert the display */
- wr_buf[2] = OLED_SET_SEGMENT_REMAP; /* Remap Columns */
- wr_buf[3] = OLED_SET_COM_DIR; /* Remap Rows */
-
- /* Select sequential COM configuration */
- wr_buf[4] = OLED_SET_COM_PINS;
- wr_buf[5] = 0x00;
- wr_buf[6] = 0xC0;
- wr_buf[7] = 0x20;
- wr_buf[8] = 0x00;
-
- /* Turn on Display */
- wr_buf[9] = OLED_DISPLAY_ON;
-
- status = spi_write(dev->spi, wr_buf, 10);
-}
-
-/**
- * SPI hardware probe. Sets correct SPI mode, attempts
- * to obtain memory needed by the driver, and performs
- * a simple initialization of the device.
- */
-static int gpio_pmodoled_spi_probe(struct spi_device *spi)
-{
- int status = 0;
- struct gpio_pmodoled_device *gpio_pmodoled_dev;
-
- /* We rely on full duplex transfers, mostly to reduce
- * per transfer overheads (by making few transfers).
- */
- if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
- status = -EINVAL;
- dev_err(&spi->dev, "SPI settings incorrect: %d\n", status);
- goto spi_err;
- }
-
- /* We must use SPI_MODE_0 */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
-
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
- spi->mode, spi->max_speed_hz / 1000,
- status);
- goto spi_err;
- }
-
- /* Get gpio_pmodoled_device structure */
- gpio_pmodoled_dev = (struct gpio_pmodoled_device *)spi->dev.platform_data;
- if (gpio_pmodoled_dev == NULL) {
- dev_err(&spi->dev, "Cannot get gpio_pmodoled_device.\n");
- status = -EINVAL;
- goto spi_platform_data_err;
- }
-
- pr_info(SPI_DRIVER_NAME " [%s] SPI Probing\n", gpio_pmodoled_dev->name);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", gpio_pmodoled_dev->name);
-#endif
-
- /* Setup char driver */
- status = gpio_pmodoled_setup_cdev(gpio_pmodoled_dev, &(gpio_pmodoled_dev->dev_id), spi);
- if (status) {
- dev_err(&spi->dev, "spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
- goto cdev_add_err;
- }
-
- /* Initialize Mutex */
- mutex_init(&gpio_pmodoled_dev->mutex);
-
- /**
- * It is important to the OLED's longevity that the lines that
- * control it's power are carefully controlled. This is a good
- * time to ensure that the device is ot turned on until it is
- * instructed to do so.
- */
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", gpio_pmodoled_dev->name);
-#endif
-
- status = gpio_pmodoled_init_gpio(gpio_pmodoled_dev);
- if (status) {
- dev_err(&spi->dev, "spi_probe: Error initializing GPIO\n");
- goto oled_init_error;
- }
-
- gpio_pmodoled_disp_init(gpio_pmodoled_dev);
-
- memset(gpio_pmodoled_dev->disp_buf, 0x00, DISPLAY_BUF_SZ);
-
- status = screen_buf_to_display(gpio_pmodoled_dev->disp_buf, gpio_pmodoled_dev);
- if (status) {
- dev_err(&spi->dev, "spi_probe: Error sending initial Display String\n");
- goto oled_init_error;
- }
- return status;
-
-oled_init_error:
- if (&gpio_pmodoled_dev->cdev)
- cdev_del(&gpio_pmodoled_dev->cdev);
-cdev_add_err:
-spi_platform_data_err:
-spi_err:
- return status;
-}
-
-static int gpio_pmodoled_spi_remove(struct spi_device *spi)
-{
- int status;
- struct gpio_pmodoled_device *dev;
- uint8_t wr_buf[10];
-
- dev = (struct gpio_pmodoled_device *)spi->dev.platform_data;
-
- if (dev == NULL) {
- dev_err(&spi->dev, "spi_remove: Error fetch gpio_pmodoled_device struct\n");
- return -EINVAL;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Clearing Display\n", dev->name);
-#endif
-
- /* Clear Display */
- memset(dev->disp_buf, 0, DISPLAY_BUF_SZ);
- status = screen_buf_to_display(dev->disp_buf, dev);
-
- /* Turn off display */
- wr_buf[0] = OLED_DISPLAY_OFF;
- status = spi_write(spi, wr_buf, 1);
- if (status)
- dev_err(&spi->dev, "oled_spi_remove: Error writing to SPI device\n");
-
- /* Turn off VCC (VBAT) */
- gpio_set_value(dev->iVBAT, 1);
- msleep(100);
- /* TUrn off VDD Power */
- gpio_set_value(dev->iVDD, 1);
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Free GPIOs\n", dev->name);
-#endif
-
- {
- struct gpio gpio_pmodoled_ctrl[] = {
- { dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat" },
- { dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD" },
- { dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET" },
- { dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C" },
- };
-
- gpio_free_array(gpio_pmodoled_ctrl, 4);
- }
-
- if (&dev->cdev) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
-#endif
- device_destroy(gpio_pmodoled_class, dev->dev_id);
- cdev_del(&dev->cdev);
- }
-
- cur_minor--;
-
- pr_info(SPI_DRIVER_NAME " [%s] spi_remove: Device Removed\n", dev->name);
-
- return status;
-}
-
-static struct spi_driver gpio_pmodoled_spi_driver = {
- .driver = {
- .name = SPI_DRIVER_NAME,
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = gpio_pmodoled_spi_probe,
- .remove = gpio_pmodoled_spi_remove,
-};
-
-static const struct of_device_id gpio_pmodoled_of_match[] = {
- { .compatible = "dglnt,pmodoled-gpio", },
- {},
-};
-MODULE_DEVICE_TABLE(of, gpio_pmodoled_of_match);
-
-/**
- * gpio_pmodoled_of_probe - Probe method for PmodOLED device (over GPIO).
- * @pdev: pointer to platform devices
- *
- * This function probes the OLED device in the device tree. It initializes the
- * OLED driver data structure. It returns 0, if the driver is bound to the OLED
- * device, or a negative value if there is an error.
- */
-static int gpio_pmodoled_of_probe(struct platform_device *pdev)
-{
- struct gpio_pmodoled_device *gpio_pmodoled_dev;
- struct platform_device *gpio_pmodoled_pdev;
- struct spi_gpio_platform_data *gpio_pmodoled_pdata;
-
- struct device_node *np = pdev->dev.of_node;
-
- const u32 *tree_info;
- int status = 0;
-
- /* Alloc Space for platform device structure */
- gpio_pmodoled_dev = kzalloc(sizeof(*gpio_pmodoled_dev), GFP_KERNEL);
- if (!gpio_pmodoled_dev) {
- status = -ENOMEM;
- goto dev_alloc_err;
- }
-
- /* Alloc Graphic Buffer for device */
- gpio_pmodoled_dev->disp_buf = kmalloc(DISPLAY_BUF_SZ, GFP_KERNEL);
- if (!gpio_pmodoled_dev->disp_buf) {
- status = -ENOMEM;
- dev_err(&pdev->dev, "Device Display data buffer allocation failed: %d\n", status);
- goto disp_buf_alloc_err;
- }
-
- /* Get the GPIO Pins */
- gpio_pmodoled_dev->iVBAT = of_get_named_gpio(np, "vbat-gpio", 0);
- gpio_pmodoled_dev->iVDD = of_get_named_gpio(np, "vdd-gpio", 0);
- gpio_pmodoled_dev->iRES = of_get_named_gpio(np, "res-gpio", 0);
- gpio_pmodoled_dev->iDC = of_get_named_gpio(np, "dc-gpio", 0);
- gpio_pmodoled_dev->iSCLK = of_get_named_gpio(np, "spi-sclk-gpio", 0);
- gpio_pmodoled_dev->iSDIN = of_get_named_gpio(np, "spi-sdin-gpio", 0);
- status = of_get_named_gpio(np, "spi-cs-gpio", 0);
- gpio_pmodoled_dev->iCS = (status < 0) ? SPI_GPIO_NO_CHIPSELECT : status;
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: iVBAT: 0x%lx\n", np->name, gpio_pmodoled_dev->iVBAT);
- pr_info(DRIVER_NAME " %s: iVDD : 0x%lx\n", np->name, gpio_pmodoled_dev->iVDD);
- pr_info(DRIVER_NAME " %s: iRES : 0x%lx\n", np->name, gpio_pmodoled_dev->iRES);
- pr_info(DRIVER_NAME " %s: iDC : 0x%lx\n", np->name, gpio_pmodoled_dev->iDC);
- pr_info(DRIVER_NAME " %s: iSCLK: 0x%lx\n", np->name, gpio_pmodoled_dev->iSCLK);
- pr_info(DRIVER_NAME " %s: iSDIN: 0x%lx\n", np->name, gpio_pmodoled_dev->iSDIN);
- pr_info(DRIVER_NAME " %s: iCS : 0x%lx\n", np->name, gpio_pmodoled_dev->iCS);
-#endif
-
- /* Get SPI Related Params */
- tree_info = of_get_property(np, "spi-bus-num", NULL);
- if (tree_info) {
- gpio_pmodoled_dev->spi_id = be32_to_cpup((tree_info));
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: BUS_ID\t%x\n", np->name, gpio_pmodoled_dev->spi_id);
-#endif
- }
-
- /* Alloc Space for platform data structure */
- gpio_pmodoled_pdata = kzalloc(sizeof(*gpio_pmodoled_pdata), GFP_KERNEL);
- if (!gpio_pmodoled_pdata) {
- status = -ENOMEM;
- goto pdata_alloc_err;
- }
-
- /* Fill up Platform Data Structure */
- gpio_pmodoled_pdata->sck = gpio_pmodoled_dev->iSCLK;
- gpio_pmodoled_pdata->miso = SPI_GPIO_NO_MISO;
- gpio_pmodoled_pdata->mosi = gpio_pmodoled_dev->iSDIN;
- gpio_pmodoled_pdata->num_chipselect = 1;
-
- /* Alloc Space for platform data structure */
- gpio_pmodoled_pdev = kzalloc(sizeof(*gpio_pmodoled_pdev), GFP_KERNEL);
- if (!gpio_pmodoled_pdev) {
- status = -ENOMEM;
- goto pdev_alloc_err;
- }
-
- /* Fill up Platform Device Structure */
- gpio_pmodoled_pdev->name = "spi_gpio";
- gpio_pmodoled_pdev->id = gpio_pmodoled_dev->spi_id;
- gpio_pmodoled_pdev->dev.platform_data = gpio_pmodoled_pdata;
- gpio_pmodoled_dev->pdev = gpio_pmodoled_pdev;
-
- /* Register spi_gpio master */
- status = platform_device_register(gpio_pmodoled_dev->pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "platform_device_register failed: %d\n", status);
- goto pdev_reg_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi_gpio platform device registered.\n", np->name);
-#endif
- gpio_pmodoled_dev->name = np->name;
-
- /* Fill up Board Info for SPI device */
- status = add_gpio_pmodoled_device_to_bus(gpio_pmodoled_dev);
- if (status < 0) {
- dev_err(&pdev->dev, "add_gpio_pmodoled_device_to_bus failed: %d\n", status);
- goto spi_add_err;
- }
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s: spi device registered.\n", np->name);
-#endif
-
- /* Point device node data to gpio_pmodoled_device structure */
- if (np->data == NULL)
- np->data = gpio_pmodoled_dev;
-
- if (gpio_pmodoled_dev_id == 0) {
- /* Alloc Major & Minor number for char device */
- status = alloc_chrdev_region(&gpio_pmodoled_dev_id, 0, MAX_PMODOLED_GPIO_DEV_NUM, DRIVER_NAME);
- if (status) {
- dev_err(&pdev->dev, "Character device region not allocated correctly: %d\n", status);
- goto err_alloc_chrdev_region;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Char Device Region Registered, with Major: %d.\n",
- MAJOR(gpio_pmodoled_dev_id));
-#endif
- }
-
- if (gpio_pmodoled_class == NULL) {
- /* Create Pmodoled-gpio Device Class */
- gpio_pmodoled_class = class_create(THIS_MODULE, DRIVER_NAME);
- if (IS_ERR(gpio_pmodoled_class)) {
- status = PTR_ERR(gpio_pmodoled_class);
- goto err_create_class;
- }
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : pmodoled_gpio device class registered.\n");
-#endif
- }
-
- if (spi_drv_registered == 0) {
- /* Register SPI Driver for Pmodoled Device */
- status = spi_register_driver(&gpio_pmodoled_spi_driver);
- if (status < 0) {
- dev_err(&pdev->dev, "gpio_pmodoled_spi_driver register failed: %d\n", status);
- goto err_spi_register;
- }
- spi_drv_registered = 1;
- }
-
- device_num++;
-
- return status;
-
-err_spi_register:
- class_destroy(gpio_pmodoled_class);
- gpio_pmodoled_class = NULL;
-err_create_class:
- unregister_chrdev_region(gpio_pmodoled_dev_id, MAX_PMODOLED_GPIO_DEV_NUM);
- gpio_pmodoled_dev_id = 0;
-err_alloc_chrdev_region:
- spi_unregister_device(gpio_pmodoled_dev->spi);
-spi_add_err:
- platform_device_unregister(gpio_pmodoled_dev->pdev);
-pdev_reg_err:
- kfree(gpio_pmodoled_pdev);
-pdev_alloc_err:
- kfree(gpio_pmodoled_pdata);
-pdata_alloc_err:
- kfree(gpio_pmodoled_dev->disp_buf);
-disp_buf_alloc_err:
- kfree(gpio_pmodoled_dev);
-dev_alloc_err:
- return status;
-}
-
-/**
- * gpio_pmodoled_of_remove - Remove method for ZED on-board OLED device.
- * @np: pointer to device tree node
- *
- * This function removes the OLED device in the device tree. It frees the
- * OLED driver data structure. It returns 0, if the driver is successfully
- * removed, or a negative value if there is an error.
- */
-static int gpio_pmodoled_of_remove(struct platform_device *pdev)
-{
- struct gpio_pmodoled_device *gpio_pmodoled_dev;
- struct device_node *np = pdev->dev.of_node;
-
- if (np->data == NULL) {
- dev_err(&pdev->dev, "pmodoled %s: ERROR: No gpio_pmodoled_device structure found!\n", np->name);
- return -ENOSYS;
- }
- gpio_pmodoled_dev = (struct gpio_pmodoled_device *)(np->data);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Free display buffer.\n", np->name);
-#endif
-
- if (gpio_pmodoled_dev->disp_buf != NULL)
- kfree(gpio_pmodoled_dev->disp_buf);
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " %s : Unregister gpio_spi Platform Devices.\n", np->name);
-#endif
-
- if (gpio_pmodoled_dev->pdev != NULL)
- platform_device_unregister(gpio_pmodoled_dev->pdev);
-
- np->data = NULL;
- device_num--;
-
- /* Unregister SPI Driver, Destroy pmodoled-gpio class, Release device id Region after
- * all pmodoled-gpio devices have been removed.
- */
- if (device_num == 0) {
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Unregister SPI Driver.\n");
-#endif
- spi_unregister_driver(&gpio_pmodoled_spi_driver);
- spi_drv_registered = 0;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Destroy pmodoled_gpio Class.\n");
-#endif
-
- if (gpio_pmodoled_class)
- class_destroy(gpio_pmodoled_class);
-
- gpio_pmodoled_class = NULL;
-
-#ifdef CONFIG_PMODS_DEBUG
- pr_info(DRIVER_NAME " : Release Char Device Region.\n");
-#endif
-
- unregister_chrdev_region(gpio_pmodoled_dev_id, MAX_PMODOLED_GPIO_DEV_NUM);
- gpio_pmodoled_dev_id = 0;
- }
-
- return 0;
-}
-
-static struct platform_driver gpio_pmodoled_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = gpio_pmodoled_of_match,
- },
- .probe = gpio_pmodoled_of_probe,
- .remove = gpio_pmodoled_of_remove,
-};
-
-module_platform_driver(gpio_pmodoled_driver);
-
-MODULE_AUTHOR("Digilent, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_NAME ": PmodOLED display driver");
-MODULE_ALIAS(DRIVER_NAME);