Browse Source

Merge tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the big set of new char/misc driver drivers and features for
  4.12-rc1.

  There's lots of new drivers added this time around, new firmware
  drivers from Google, more auxdisplay drivers, extcon drivers, fpga
  drivers, and a bunch of other driver updates. Nothing major, except if
  you happen to have the hardware for these drivers, and then you will
  be happy :)

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (136 commits)
  firmware: google memconsole: Fix return value check in platform_memconsole_init()
  firmware: Google VPD: Fix return value check in vpd_platform_init()
  goldfish_pipe: fix build warning about using too much stack.
  goldfish_pipe: An implementation of more parallel pipe
  fpga fr br: update supported version numbers
  fpga: region: release FPGA region reference in error path
  fpga altera-hps2fpga: disable/unprepare clock on error in alt_fpga_bridge_probe()
  mei: drop the TODO from samples
  firmware: Google VPD sysfs driver
  firmware: Google VPD: import lib_vpd source files
  misc: lkdtm: Add volatile to intentional NULL pointer reference
  eeprom: idt_89hpesx: Add OF device ID table
  misc: ds1682: Add OF device ID table
  misc: tsl2550: Add OF device ID table
  w1: Remove unneeded use of assert() and remove w1_log.h
  w1: Use kernel common min() implementation
  uio_mf624: Align memory regions to page size and set correct offsets
  uio_mf624: Refactor memory info initialization
  uio: Allow handling of non page-aligned memory regions
  hangcheck-timer: Fix typo in comment
  ...
Linus Torvalds 8 years ago
parent
commit
af82455f7d
100 changed files with 5308 additions and 1925 deletions
  1. 0 3
      Documentation/DocBook/rapidio.tmpl
  2. 45 0
      Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt
  3. 33 0
      Documentation/devicetree/bindings/firmware/coreboot.txt
  4. 12 0
      Documentation/devicetree/bindings/fpga/altera-pr-ip.txt
  5. 1 0
      Documentation/devicetree/bindings/fpga/fpga-region.txt
  6. 21 0
      Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
  7. 44 0
      Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt
  8. 5 1
      Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt
  9. 22 0
      Documentation/devicetree/bindings/nvmem/imx-iim.txt
  10. 5 0
      Documentation/devicetree/bindings/nvmem/imx-ocotp.txt
  11. 93 270
      Documentation/driver-api/vme.rst
  12. 0 123
      Documentation/extcon/porting-android-switch-class
  13. 4 0
      Documentation/w1/slaves/00-INDEX
  14. 50 0
      Documentation/w1/slaves/w1_ds2413
  15. 63 0
      Documentation/w1/slaves/w1_ds2438
  16. 4 4
      MAINTAINERS
  17. 26 0
      arch/arm/mach-ep93xx/ts72xx.c
  18. 1 1
      arch/x86/hyperv/hv_init.c
  19. 6 1
      arch/x86/include/uapi/asm/hyperv.h
  20. 3 0
      arch/x86/kernel/cpu/mshyperv.c
  21. 1 1
      drivers/android/Kconfig
  22. 304 0
      drivers/auxdisplay/Kconfig
  23. 4 0
      drivers/auxdisplay/Makefile
  24. 0 0
      drivers/auxdisplay/arm-charlcd.c
  25. 818 0
      drivers/auxdisplay/charlcd.c
  26. 326 0
      drivers/auxdisplay/hd44780.c
  27. 15 5
      drivers/auxdisplay/ht16k33.c
  28. 1 0
      drivers/auxdisplay/img-ascii-lcd.c
  29. 92 735
      drivers/auxdisplay/panel.c
  30. 1 1
      drivers/char/hangcheck-timer.c
  31. 1 1
      drivers/char/hpet.c
  32. 6 5
      drivers/char/misc.c
  33. 5 4
      drivers/char/mspec.c
  34. 14 40
      drivers/char/tpm/tpm-chip.c
  35. 1 1
      drivers/char/virtio_console.c
  36. 16 17
      drivers/dax/dax.c
  37. 7 0
      drivers/extcon/Kconfig
  38. 1 0
      drivers/extcon/Makefile
  39. 46 0
      drivers/extcon/extcon-arizona.c
  40. 395 0
      drivers/extcon/extcon-intel-cht-wc.c
  41. 6 0
      drivers/extcon/extcon-palmas.c
  42. 4 6
      drivers/extcon/extcon-usb-gpio.c
  43. 1 21
      drivers/extcon/extcon.c
  44. 1 1
      drivers/firewire/core-topology.c
  45. 4 4
      drivers/firewire/core.h
  46. 51 8
      drivers/firmware/google/Kconfig
  47. 9 1
      drivers/firmware/google/Makefile
  48. 88 0
      drivers/firmware/google/coreboot_table-acpi.c
  49. 82 0
      drivers/firmware/google/coreboot_table-of.c
  50. 94 0
      drivers/firmware/google/coreboot_table.c
  51. 50 0
      drivers/firmware/google/coreboot_table.h
  52. 109 0
      drivers/firmware/google/memconsole-coreboot.c
  53. 153 0
      drivers/firmware/google/memconsole-x86-legacy.c
  54. 23 132
      drivers/firmware/google/memconsole.c
  55. 43 0
      drivers/firmware/google/memconsole.h
  56. 332 0
      drivers/firmware/google/vpd.c
  57. 99 0
      drivers/firmware/google/vpd_decode.c
  58. 58 0
      drivers/firmware/google/vpd_decode.h
  59. 42 0
      drivers/fpga/Kconfig
  60. 6 0
      drivers/fpga/Makefile
  61. 20 12
      drivers/fpga/altera-freeze-bridge.c
  62. 9 6
      drivers/fpga/altera-hps2fpga.c
  63. 68 0
      drivers/fpga/altera-pr-ip-core-plat.c
  64. 220 0
      drivers/fpga/altera-pr-ip-core.c
  65. 5 12
      drivers/fpga/fpga-bridge.c
  66. 1 1
      drivers/fpga/fpga-mgr.c
  67. 12 3
      drivers/fpga/fpga-region.c
  68. 207 0
      drivers/fpga/ice40-spi.c
  69. 156 0
      drivers/fpga/ts73xx-fpga.c
  70. 161 0
      drivers/fpga/xilinx-pr-decoupler.c
  71. 198 0
      drivers/fpga/xilinx-spi.c
  72. 25 3
      drivers/fpga/zynq-fpga.c
  73. 8 15
      drivers/gpio/gpiolib.c
  74. 5 5
      drivers/hv/channel.c
  75. 24 24
      drivers/hv/channel_mgmt.c
  76. 34 31
      drivers/hv/connection.c
  77. 4 1
      drivers/hv/hv.c
  78. 0 2
      drivers/hv/hv_balloon.c
  79. 0 2
      drivers/hv/hv_fcopy.c
  80. 5 7
      drivers/hv/hv_kvp.c
  81. 0 2
      drivers/hv/hv_snapshot.c
  82. 9 20
      drivers/hv/hyperv_vmbus.h
  83. 8 14
      drivers/hv/ring_buffer.c
  84. 1 3
      drivers/hv/vmbus_drv.c
  85. 1 1
      drivers/hwtracing/coresight/of_coresight.c
  86. 4 11
      drivers/iio/industrialio-core.c
  87. 18 17
      drivers/infiniband/core/ucm.c
  88. 2 2
      drivers/infiniband/core/user_mad.c
  89. 1 1
      drivers/infiniband/core/uverbs_main.c
  90. 1 1
      drivers/infiniband/hw/hfi1/device.c
  91. 2 9
      drivers/input/evdev.c
  92. 2 9
      drivers/input/joydev.c
  93. 2 9
      drivers/input/mousedev.c
  94. 4 12
      drivers/media/cec/cec-core.c
  95. 5 15
      drivers/media/media-devnode.c
  96. 6 287
      drivers/misc/Kconfig
  97. 3 2
      drivers/misc/Makefile
  98. 266 0
      drivers/misc/aspeed-lpc-ctrl.c
  99. 7 0
      drivers/misc/ds1682.c
  100. 57 0
      drivers/misc/eeprom/idt_89hpesx.c

+ 0 - 3
Documentation/DocBook/rapidio.tmpl

@@ -128,9 +128,6 @@
      </sect1>
      </sect1>
      <sect1 id="Device_model_support"><title>Device model support</title>
      <sect1 id="Device_model_support"><title>Device model support</title>
 !Idrivers/rapidio/rio-driver.c
 !Idrivers/rapidio/rio-driver.c
-     </sect1>
-     <sect1 id="Sysfs_support"><title>Sysfs support</title>
-!Idrivers/rapidio/rio-sysfs.c
      </sect1>
      </sect1>
      <sect1 id="PPC32_support"><title>PPC32 support</title>
      <sect1 id="PPC32_support"><title>PPC32 support</title>
 !Iarch/powerpc/sysdev/fsl_rio.c
 !Iarch/powerpc/sysdev/fsl_rio.c

+ 45 - 0
Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt

@@ -0,0 +1,45 @@
+DT bindings for the Hitachi HD44780 Character LCD Controller
+
+The Hitachi HD44780 Character LCD Controller is commonly used on character LCDs
+that can display one or more lines of text. It exposes an M6800 bus interface,
+which can be used in either 4-bit or 8-bit mode.
+
+Required properties:
+  - compatible: Must contain "hit,hd44780",
+  - data-gpios: Must contain an array of either 4 or 8 GPIO specifiers,
+    referring to the GPIO pins connected to the data signal lines DB0-DB7
+    (8-bit mode) or DB4-DB7 (4-bit mode) of the LCD Controller's bus interface,
+  - enable-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "E" (Enable) signal line of the LCD Controller's bus
+    interface,
+  - rs-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "RS" (Register Select) signal line of the LCD Controller's
+    bus interface,
+  - display-height-chars: Height of the display, in character cells,
+  - display-width-chars: Width of the display, in character cells.
+
+Optional properties:
+  - rw-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "RW" (Read/Write) signal line of the LCD Controller's bus
+    interface,
+  - backlight-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    used for enabling the LCD's backlight,
+  - internal-buffer-width: Internal buffer width (default is 40 for displays
+    with 1 or 2 lines, and display-width-chars for displays with more than 2
+    lines).
+
+Example:
+
+	auxdisplay {
+		compatible = "hit,hd44780";
+
+		data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>,
+			     <&hc595 1 GPIO_ACTIVE_HIGH>,
+			     <&hc595 2 GPIO_ACTIVE_HIGH>,
+			     <&hc595 3 GPIO_ACTIVE_HIGH>;
+		enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>;
+		rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>;
+
+		display-height-chars = <2>;
+		display-width-chars = <16>;
+	};

+ 33 - 0
Documentation/devicetree/bindings/firmware/coreboot.txt

@@ -0,0 +1,33 @@
+COREBOOT firmware information
+
+The device tree node to communicate the location of coreboot's memory-resident
+bookkeeping structures to the kernel. Since coreboot itself cannot boot a
+device-tree-based kernel (yet), this node needs to be inserted by a
+second-stage bootloader (a coreboot "payload").
+
+Required properties:
+ - compatible: Should be "coreboot"
+ - reg: Address and length of the following two memory regions, in order:
+	1.) The coreboot table. This is a list of variable-sized descriptors
+	that contain various compile- and run-time generated firmware
+	parameters. It is identified by the magic string "LBIO" in its first
+	four bytes.
+	See coreboot's src/commonlib/include/commonlib/coreboot_tables.h for
+	details.
+	2.) The CBMEM area. This is a downward-growing memory region used by
+	coreboot to dynamically allocate data structures that remain resident.
+	It may or may not include the coreboot table as one of its members. It
+	is identified by a root node descriptor with the magic number
+	0xc0389481 that resides in the topmost 8 bytes of the area.
+	See coreboot's src/include/imd.h for details.
+
+Example:
+	firmware {
+		ranges;
+
+		coreboot {
+			compatible = "coreboot";
+			reg = <0xfdfea000 0x264>,
+			      <0xfdfea000 0x16000>;
+		}
+	};

+ 12 - 0
Documentation/devicetree/bindings/fpga/altera-pr-ip.txt

@@ -0,0 +1,12 @@
+Altera Arria10 Partial Reconfiguration IP
+
+Required properties:
+- compatible : should contain "altr,a10-pr-ip"
+- reg        : base address and size for memory mapped io.
+
+Example:
+
+	fpga_mgr: fpga-mgr@ff20c000 {
+		compatible = "altr,a10-pr-ip";
+		reg = <0xff20c000 0x10>;
+	};

+ 1 - 0
Documentation/devicetree/bindings/fpga/fpga-region.txt

@@ -186,6 +186,7 @@ Optional properties:
 	otherwise full reconfiguration is done.
 	otherwise full reconfiguration is done.
 - external-fpga-config : boolean, set if the FPGA has already been configured
 - external-fpga-config : boolean, set if the FPGA has already been configured
 	prior to OS boot up.
 	prior to OS boot up.
+- encrypted-fpga-config : boolean, set if the bitstream is encrypted
 - region-unfreeze-timeout-us : The maximum time in microseconds to wait for
 - region-unfreeze-timeout-us : The maximum time in microseconds to wait for
 	bridges to successfully become enabled after the region has been
 	bridges to successfully become enabled after the region has been
 	programmed.
 	programmed.

+ 21 - 0
Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt

@@ -0,0 +1,21 @@
+Lattice iCE40 FPGA Manager
+
+Required properties:
+- compatible:		Should contain "lattice,ice40-fpga-mgr"
+- reg:			SPI chip select
+- spi-max-frequency:	Maximum SPI frequency (>=1000000, <=25000000)
+- cdone-gpios:		GPIO input connected to CDONE pin
+- reset-gpios:		Active-low GPIO output connected to CRESET_B pin. Note
+			that unless the GPIO is held low during startup, the
+			FPGA will enter Master SPI mode and drive SCK with a
+			clock signal potentially jamming other devices on the
+			bus until the firmware is loaded.
+
+Example:
+	fpga: fpga@0 {
+		compatible = "lattice,ice40-fpga-mgr";
+		reg = <0>;
+		spi-max-frequency = <1000000>;
+		cdone-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
+	};

+ 44 - 0
Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt

@@ -0,0 +1,44 @@
+Xilinx Slave Serial SPI FPGA Manager
+
+Xilinx Spartan-6 FPGAs support a method of loading the bitstream over
+what is referred to as "slave serial" interface.
+The slave serial link is not technically SPI, and might require extra
+circuits in order to play nicely with other SPI slaves on the same bus.
+
+See https://www.xilinx.com/support/documentation/user_guides/ug380.pdf
+
+Required properties:
+- compatible: should contain "xlnx,fpga-slave-serial"
+- reg: spi chip select of the FPGA
+- prog_b-gpios: config pin (referred to as PROGRAM_B in the manual)
+- done-gpios: config status pin (referred to as DONE in the manual)
+
+Example for full FPGA configuration:
+
+	fpga-region0 {
+		compatible = "fpga-region";
+		fpga-mgr = <&fpga_mgr_spi>;
+		#address-cells = <0x1>;
+		#size-cells = <0x1>;
+	};
+
+	spi1: spi@10680 {
+		compatible = "marvell,armada-xp-spi", "marvell,orion-spi";
+		pinctrl-0 = <&spi0_pins>;
+		pinctrl-names = "default";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		cell-index = <1>;
+		interrupts = <92>;
+		clocks = <&coreclk 0>;
+		status = "okay";
+
+		fpga_mgr_spi: fpga-mgr@0 {
+			compatible = "xlnx,fpga-slave-serial";
+			spi-max-frequency = <60000000>;
+			spi-cpha;
+			reg = <0>;
+			done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
+			prog_b-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
+		};
+	};

+ 5 - 1
Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt

@@ -1,7 +1,11 @@
 Allwinner sunxi-sid
 Allwinner sunxi-sid
 
 
 Required properties:
 Required properties:
-- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
+- compatible: Should be one of the following:
+  "allwinner,sun4i-a10-sid"
+  "allwinner,sun7i-a20-sid"
+  "allwinner,sun8i-h3-sid"
+
 - reg: Should contain registers location and length
 - reg: Should contain registers location and length
 
 
 = Data cells =
 = Data cells =

+ 22 - 0
Documentation/devicetree/bindings/nvmem/imx-iim.txt

@@ -0,0 +1,22 @@
+Freescale i.MX IC Identification Module (IIM) device tree bindings
+
+This binding represents the IC Identification Module (IIM) found on
+i.MX25, i.MX27, i.MX31, i.MX35, i.MX51 and i.MX53 SoCs.
+
+Required properties:
+- compatible: should be one of
+	"fsl,imx25-iim", "fsl,imx27-iim",
+	"fsl,imx31-iim", "fsl,imx35-iim",
+	"fsl,imx51-iim", "fsl,imx53-iim",
+- reg: Should contain the register base and length.
+- interrupts: Should contain the interrupt for the IIM
+- clocks: Should contain a phandle pointing to the gated peripheral clock.
+
+Example:
+
+	iim: iim@63f98000 {
+		compatible = "fsl,imx53-iim", "fsl,imx27-iim";
+		reg = <0x63f98000 0x4000>;
+		interrupts = <69>;
+                clocks = <&clks IMX5_CLK_IIM_GATE>;
+	};

+ 5 - 0
Documentation/devicetree/bindings/nvmem/imx-ocotp.txt

@@ -9,14 +9,19 @@ Required properties:
 	"fsl,imx6sl-ocotp" (i.MX6SL), or
 	"fsl,imx6sl-ocotp" (i.MX6SL), or
 	"fsl,imx6sx-ocotp" (i.MX6SX),
 	"fsl,imx6sx-ocotp" (i.MX6SX),
 	"fsl,imx6ul-ocotp" (i.MX6UL),
 	"fsl,imx6ul-ocotp" (i.MX6UL),
+	"fsl,imx7d-ocotp" (i.MX7D/S),
 	followed by "syscon".
 	followed by "syscon".
 - reg: Should contain the register base and length.
 - reg: Should contain the register base and length.
 - clocks: Should contain a phandle pointing to the gated peripheral clock.
 - clocks: Should contain a phandle pointing to the gated peripheral clock.
 
 
+Optional properties:
+- read-only: disable write access
+
 Example:
 Example:
 
 
 	ocotp: ocotp@021bc000 {
 	ocotp: ocotp@021bc000 {
 		compatible = "fsl,imx6q-ocotp", "syscon";
 		compatible = "fsl,imx6q-ocotp", "syscon";
 		reg = <0x021bc000 0x4000>;
 		reg = <0x021bc000 0x4000>;
 		clocks = <&clks IMX6QDL_CLK_IIM>;
 		clocks = <&clks IMX6QDL_CLK_IIM>;
+		read-only;
 	};
 	};

+ 93 - 270
Documentation/driver-api/vme.rst

@@ -6,36 +6,15 @@ Driver registration
 
 
 As with other subsystems within the Linux kernel, VME device drivers register
 As with other subsystems within the Linux kernel, VME device drivers register
 with the VME subsystem, typically called from the devices init routine.  This is
 with the VME subsystem, typically called from the devices init routine.  This is
-achieved via a call to the following function:
+achieved via a call to :c:func:`vme_register_driver`.
 
 
-.. code-block:: c
-
-	int vme_register_driver (struct vme_driver *driver, unsigned int ndevs);
+A pointer to a structure of type :c:type:`struct vme_driver <vme_driver>` must
+be provided to the registration function. Along with the maximum number of
+devices your driver is able to support.
 
 
-If driver registration is successful this function returns zero, if an error
-occurred a negative error code will be returned.
-
-A pointer to a structure of type 'vme_driver' must be provided to the
-registration function. Along with ndevs, which is the number of devices your
-driver is able to support. The structure is as follows:
-
-.. code-block:: c
-
-	struct vme_driver {
-		struct list_head node;
-		const char *name;
-		int (*match)(struct vme_dev *);
-		int (*probe)(struct vme_dev *);
-		int (*remove)(struct vme_dev *);
-		void (*shutdown)(void);
-		struct device_driver driver;
-		struct list_head devices;
-		unsigned int ndev;
-	};
-
-At the minimum, the '.name', '.match' and '.probe' elements of this structure
-should be correctly set. The '.name' element is a pointer to a string holding
-the device driver's name.
+At the minimum, the '.name', '.match' and '.probe' elements of
+:c:type:`struct vme_driver <vme_driver>` should be correctly set. The '.name'
+element is a pointer to a string holding the device driver's name.
 
 
 The '.match' function allows control over which VME devices should be registered
 The '.match' function allows control over which VME devices should be registered
 with the driver. The match function should return 1 if a device should be
 with the driver. The match function should return 1 if a device should be
@@ -54,29 +33,16 @@ the number of devices probed to one:
 	}
 	}
 
 
 The '.probe' element should contain a pointer to the probe routine. The
 The '.probe' element should contain a pointer to the probe routine. The
-probe routine is passed a 'struct vme_dev' pointer as an argument. The
-'struct vme_dev' structure looks like the following:
-
-.. code-block:: c
-
-	struct vme_dev {
-		int num;
-		struct vme_bridge *bridge;
-		struct device dev;
-		struct list_head drv_list;
-		struct list_head bridge_list;
-	};
+probe routine is passed a :c:type:`struct vme_dev <vme_dev>` pointer as an
+argument.
 
 
 Here, the 'num' field refers to the sequential device ID for this specific
 Here, the 'num' field refers to the sequential device ID for this specific
 driver. The bridge number (or bus number) can be accessed using
 driver. The bridge number (or bus number) can be accessed using
 dev->bridge->num.
 dev->bridge->num.
 
 
-A function is also provided to unregister the driver from the VME core and is
-usually called from the device driver's exit routine:
-
-.. code-block:: c
-
-	void vme_unregister_driver (struct vme_driver *driver);
+A function is also provided to unregister the driver from the VME core called
+:c:func:`vme_unregister_driver` and should usually be called from the device
+driver's exit routine.
 
 
 
 
 Resource management
 Resource management
@@ -90,47 +56,29 @@ driver is called. The probe routine is passed a pointer to the devices
 device structure. This pointer should be saved, it will be required for
 device structure. This pointer should be saved, it will be required for
 requesting VME resources.
 requesting VME resources.
 
 
-The driver can request ownership of one or more master windows, slave windows
-and/or dma channels. Rather than allowing the device driver to request a
-specific window or DMA channel (which may be used by a different driver) this
-driver allows a resource to be assigned based on the required attributes of the
-driver in question:
-
-.. code-block:: c
-
-	struct vme_resource * vme_master_request(struct vme_dev *dev,
-		u32 aspace, u32 cycle, u32 width);
-
-	struct vme_resource * vme_slave_request(struct vme_dev *dev, u32 aspace,
-		u32 cycle);
-
-	struct vme_resource *vme_dma_request(struct vme_dev *dev, u32 route);
-
-For slave windows these attributes are split into the VME address spaces that
-need to be accessed in 'aspace' and VME bus cycle types required in 'cycle'.
-Master windows add a further set of attributes in 'width' specifying the
-required data transfer widths. These attributes are defined as bitmasks and as
-such any combination of the attributes can be requested for a single window,
-the core will assign a window that meets the requirements, returning a pointer
-of type vme_resource that should be used to identify the allocated resource
-when it is used. For DMA controllers, the request function requires the
-potential direction of any transfers to be provided in the route attributes.
-This is typically VME-to-MEM and/or MEM-to-VME, though some hardware can
-support VME-to-VME and MEM-to-MEM transfers as well as test pattern generation.
-If an unallocated window fitting the requirements can not be found a NULL
-pointer will be returned.
+The driver can request ownership of one or more master windows
+(:c:func:`vme_master_request`), slave windows (:c:func:`vme_slave_request`)
+and/or dma channels (:c:func:`vme_dma_request`). Rather than allowing the device
+driver to request a specific window or DMA channel (which may be used by a
+different driver) the API allows a resource to be assigned based on the required
+attributes of the driver in question. For slave windows these attributes are
+split into the VME address spaces that need to be accessed in 'aspace' and VME
+bus cycle types required in 'cycle'. Master windows add a further set of
+attributes in 'width' specifying the required data transfer widths. These
+attributes are defined as bitmasks and as such any combination of the
+attributes can be requested for a single window, the core will assign a window
+that meets the requirements, returning a pointer of type vme_resource that
+should be used to identify the allocated resource when it is used. For DMA
+controllers, the request function requires the potential direction of any
+transfers to be provided in the route attributes. This is typically VME-to-MEM
+and/or MEM-to-VME, though some hardware can support VME-to-VME and MEM-to-MEM
+transfers as well as test pattern generation. If an unallocated window fitting
+the requirements can not be found a NULL pointer will be returned.
 
 
 Functions are also provided to free window allocations once they are no longer
 Functions are also provided to free window allocations once they are no longer
-required. These functions should be passed the pointer to the resource provided
-during resource allocation:
-
-.. code-block:: c
-
-	void vme_master_free(struct vme_resource *res);
-
-	void vme_slave_free(struct vme_resource *res);
-
-	void vme_dma_free(struct vme_resource *res);
+required. These functions (:c:func:`vme_master_free`, :c:func:`vme_slave_free`
+and :c:func:`vme_dma_free`) should be passed the pointer to the resource
+provided during resource allocation.
 
 
 
 
 Master windows
 Master windows
@@ -144,61 +92,22 @@ the underlying chipset. A window must be configured before it can be used.
 Master window configuration
 Master window configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-Once a master window has been assigned the following functions can be used to
-configure it and retrieve the current settings:
-
-.. code-block:: c
-
-	int vme_master_set (struct vme_resource *res, int enabled,
-		unsigned long long base, unsigned long long size, u32 aspace,
-		u32 cycle, u32 width);
-
-	int vme_master_get (struct vme_resource *res, int *enabled,
-		unsigned long long *base, unsigned long long *size, u32 *aspace,
-		u32 *cycle, u32 *width);
-
-The address spaces, transfer widths and cycle types are the same as described
+Once a master window has been assigned :c:func:`vme_master_set` can be used to
+configure it and :c:func:`vme_master_get` to retrieve the current settings. The
+address spaces, transfer widths and cycle types are the same as described
 under resource management, however some of the options are mutually exclusive.
 under resource management, however some of the options are mutually exclusive.
 For example, only one address space may be specified.
 For example, only one address space may be specified.
 
 
-These functions return 0 on success or an error code should the call fail.
-
 
 
 Master window access
 Master window access
 ~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~
 
 
-The following functions can be used to read from and write to configured master
-windows. These functions return the number of bytes copied:
-
-.. code-block:: c
-
-	ssize_t vme_master_read(struct vme_resource *res, void *buf,
-		size_t count, loff_t offset);
-
-	ssize_t vme_master_write(struct vme_resource *res, void *buf,
-		size_t count, loff_t offset);
-
-In addition to simple reads and writes, a function is provided to do a
-read-modify-write transaction. This function returns the original value of the
-VME bus location :
-
-.. code-block:: c
-
-	unsigned int vme_master_rmw (struct vme_resource *res,
-		unsigned int mask, unsigned int compare, unsigned int swap,
-		loff_t offset);
-
-This functions by reading the offset, applying the mask. If the bits selected in
-the mask match with the values of the corresponding bits in the compare field,
-the value of swap is written the specified offset.
-
-Parts of a VME window can be mapped into user space memory using the following
-function:
+The function :c:func:`vme_master_read` can be used to read from and
+:c:func:`vme_master_write` used to write to configured master windows.
 
 
-.. code-block:: c
-
-	int vme_master_mmap(struct vme_resource *resource,
-		struct vm_area_struct *vma)
+In addition to simple reads and writes, :c:func:`vme_master_rmw` is provided to
+do a read-modify-write transaction. Parts of a VME window can also be mapped
+into user space memory using :c:func:`vme_master_mmap`.
 
 
 
 
 Slave windows
 Slave windows
@@ -213,41 +122,23 @@ it can be used.
 Slave window configuration
 Slave window configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-Once a slave window has been assigned the following functions can be used to
-configure it and retrieve the current settings:
-
-.. code-block:: c
-
-	int vme_slave_set (struct vme_resource *res, int enabled,
-		unsigned long long base, unsigned long long size,
-		dma_addr_t mem, u32 aspace, u32 cycle);
-
-	int vme_slave_get (struct vme_resource *res, int *enabled,
-		unsigned long long *base, unsigned long long *size,
-		dma_addr_t *mem, u32 *aspace, u32 *cycle);
+Once a slave window has been assigned :c:func:`vme_slave_set` can be used to
+configure it and :c:func:`vme_slave_get` to retrieve the current settings.
 
 
 The address spaces, transfer widths and cycle types are the same as described
 The address spaces, transfer widths and cycle types are the same as described
 under resource management, however some of the options are mutually exclusive.
 under resource management, however some of the options are mutually exclusive.
 For example, only one address space may be specified.
 For example, only one address space may be specified.
 
 
-These functions return 0 on success or an error code should the call fail.
-
 
 
 Slave window buffer allocation
 Slave window buffer allocation
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-Functions are provided to allow the user to allocate and free a contiguous
-buffers which will be accessible by the VME bridge. These functions do not have
-to be used, other methods can be used to allocate a buffer, though care must be
-taken to ensure that they are contiguous and accessible by the VME bridge:
-
-.. code-block:: c
-
-	void * vme_alloc_consistent(struct vme_resource *res, size_t size,
-		dma_addr_t *mem);
-
-	void vme_free_consistent(struct vme_resource *res, size_t size,
-		void *virt,	dma_addr_t mem);
+Functions are provided to allow the user to allocate
+(:c:func:`vme_alloc_consistent`) and free (:c:func:`vme_free_consistent`)
+contiguous buffers which will be accessible by the VME bridge. These functions
+do not have to be used, other methods can be used to allocate a buffer, though
+care must be taken to ensure that they are contiguous and accessible by the VME
+bridge.
 
 
 
 
 Slave window access
 Slave window access
@@ -269,29 +160,18 @@ executed, reused and destroyed.
 List Management
 List Management
 ~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~
 
 
-The following functions are provided to create and destroy DMA lists. Execution
-of a list will not automatically destroy the list, thus enabling a list to be
-reused for repetitive tasks:
-
-.. code-block:: c
-
-	struct vme_dma_list *vme_new_dma_list(struct vme_resource *res);
-
-	int vme_dma_list_free(struct vme_dma_list *list);
+The function :c:func:`vme_new_dma_list` is provided to create and
+:c:func:`vme_dma_list_free` to destroy DMA lists. Execution of a list will not
+automatically destroy the list, thus enabling a list to be reused for repetitive
+tasks.
 
 
 
 
 List Population
 List Population
 ~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~
 
 
-An item can be added to a list using the following function ( the source and
+An item can be added to a list using :c:func:`vme_dma_list_add` (the source and
 destination attributes need to be created before calling this function, this is
 destination attributes need to be created before calling this function, this is
-covered under "Transfer Attributes"):
-
-.. code-block:: c
-
-	int vme_dma_list_add(struct vme_dma_list *list,
-		struct vme_dma_attr *src, struct vme_dma_attr *dest,
-		size_t count);
+covered under "Transfer Attributes").
 
 
 .. note::
 .. note::
 
 
@@ -310,41 +190,19 @@ an item to a list. This is due to the diverse attributes required for each type
 of source and destination. There are functions to create attributes for PCI, VME
 of source and destination. There are functions to create attributes for PCI, VME
 and pattern sources and destinations (where appropriate):
 and pattern sources and destinations (where appropriate):
 
 
-Pattern source:
-
-.. code-block:: c
-
-	struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, u32 type);
-
-PCI source or destination:
-
-.. code-block:: c
-
-	struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t mem);
-
-VME source or destination:
+ - PCI source or destination: :c:func:`vme_dma_pci_attribute`
+ - VME source or destination: :c:func:`vme_dma_vme_attribute`
+ - Pattern source: :c:func:`vme_dma_pattern_attribute`
 
 
-.. code-block:: c
-
-	struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long base,
-		u32 aspace, u32 cycle, u32 width);
-
-The following function should be used to free an attribute:
-
-.. code-block:: c
-
-	void vme_dma_free_attribute(struct vme_dma_attr *attr);
+The function :c:func:`vme_dma_free_attribute` should be used to free an
+attribute.
 
 
 
 
 List Execution
 List Execution
 ~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~
 
 
-The following function queues a list for execution. The function will return
-once the list has been executed:
-
-.. code-block:: c
-
-	int vme_dma_list_exec(struct vme_dma_list *list);
+The function :c:func:`vme_dma_list_exec` queues a list for execution and will
+return once the list has been executed.
 
 
 
 
 Interrupts
 Interrupts
@@ -358,20 +216,13 @@ specific VME level and status IDs.
 Attaching Interrupt Handlers
 Attaching Interrupt Handlers
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-The following functions can be used to attach and free a specific VME level and
-status ID combination. Any given combination can only be assigned a single
-callback function. A void pointer parameter is provided, the value of which is
-passed to the callback function, the use of this pointer is user undefined:
-
-.. code-block:: c
-
-	int vme_irq_request(struct vme_dev *dev, int level, int statid,
-		void (*callback)(int, int, void *), void *priv);
-
-	void vme_irq_free(struct vme_dev *dev, int level, int statid);
-
-The callback parameters are as follows. Care must be taken in writing a callback
-function, callback functions run in interrupt context:
+The function :c:func:`vme_irq_request` can be used to attach and
+:c:func:`vme_irq_free` to free a specific VME level and status ID combination.
+Any given combination can only be assigned a single callback function. A void
+pointer parameter is provided, the value of which is passed to the callback
+function, the use of this pointer is user undefined. The callback parameters are
+as follows. Care must be taken in writing a callback function, callback
+functions run in interrupt context:
 
 
 .. code-block:: c
 .. code-block:: c
 
 
@@ -381,12 +232,8 @@ function, callback functions run in interrupt context:
 Interrupt Generation
 Interrupt Generation
 ~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~
 
 
-The following function can be used to generate a VME interrupt at a given VME
-level and VME status ID:
-
-.. code-block:: c
-
-	int vme_irq_generate(struct vme_dev *dev, int level, int statid);
+The function :c:func:`vme_irq_generate` can be used to generate a VME interrupt
+at a given VME level and VME status ID.
 
 
 
 
 Location monitors
 Location monitors
@@ -399,54 +246,29 @@ monitor.
 Location Monitor Management
 Location Monitor Management
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-The following functions are provided to request the use of a block of location
-monitors and to free them after they are no longer required:
-
-.. code-block:: c
-
-	struct vme_resource * vme_lm_request(struct vme_dev *dev);
-
-	void vme_lm_free(struct vme_resource * res);
-
-Each block may provide a number of location monitors, monitoring adjacent
-locations. The following function can be used to determine how many locations
-are provided:
-
-.. code-block:: c
-
-	int vme_lm_count(struct vme_resource * res);
+The function :c:func:`vme_lm_request` is provided to request the use of a block
+of location monitors and :c:func:`vme_lm_free` to free them after they are no
+longer required. Each block may provide a number of location monitors,
+monitoring adjacent locations. The function :c:func:`vme_lm_count` can be used
+to determine how many locations are provided.
 
 
 
 
 Location Monitor Configuration
 Location Monitor Configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-Once a bank of location monitors has been allocated, the following functions
-are provided to configure the location and mode of the location monitor:
-
-.. code-block:: c
-
-	int vme_lm_set(struct vme_resource *res, unsigned long long base,
-		u32 aspace, u32 cycle);
-
-	int vme_lm_get(struct vme_resource *res, unsigned long long *base,
-		u32 *aspace, u32 *cycle);
+Once a bank of location monitors has been allocated, the function
+:c:func:`vme_lm_set` is provided to configure the location and mode of the
+location monitor. The function :c:func:`vme_lm_get` can be used to retrieve
+existing settings.
 
 
 
 
 Location Monitor Use
 Location Monitor Use
 ~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~
 
 
-The following functions allow a callback to be attached and detached from each
-location monitor location. Each location monitor can monitor a number of
-adjacent locations:
-
-.. code-block:: c
-
-	int vme_lm_attach(struct vme_resource *res, int num,
-		void (*callback)(void *));
-
-	int vme_lm_detach(struct vme_resource *res, int num);
-
-The callback function is declared as follows.
+The function :c:func:`vme_lm_attach` enables a callback to be attached and
+:c:func:`vme_lm_detach` allows on to be detached from each location monitor
+location. Each location monitor can monitor a number of adjacent locations. The
+callback function is declared as follows.
 
 
 .. code-block:: c
 .. code-block:: c
 
 
@@ -456,19 +278,20 @@ The callback function is declared as follows.
 Slot Detection
 Slot Detection
 --------------
 --------------
 
 
-This function returns the slot ID of the provided bridge.
-
-.. code-block:: c
-
-	int vme_slot_num(struct vme_dev *dev);
+The function :c:func:`vme_slot_num` returns the slot ID of the provided bridge.
 
 
 
 
 Bus Detection
 Bus Detection
 -------------
 -------------
 
 
-This function returns the bus ID of the provided bridge.
+The function :c:func:`vme_bus_num` returns the bus ID of the provided bridge.
 
 
-.. code-block:: c
 
 
-	int vme_bus_num(struct vme_dev *dev);
+VME API
+-------
+
+.. kernel-doc:: include/linux/vme.h
+   :internal:
 
 
+.. kernel-doc:: drivers/vme/vme.c
+   :export:

+ 0 - 123
Documentation/extcon/porting-android-switch-class

@@ -1,123 +0,0 @@
-
-	Staging/Android Switch Class Porting Guide
-	(linux/drivers/staging/android/switch)
-	(c) Copyright 2012 Samsung Electronics
-
-AUTHORS
-MyungJoo Ham <myungjoo.ham@samsung.com>
-
-/*****************************************************************
- * CHAPTER 1.                                                    *
- * PORTING SWITCH CLASS DEVICE DRIVERS                           *
- *****************************************************************/
-
-****** STEP 1. Basic Functionality
-	No extcon extended feature, but switch features only.
-
-- struct switch_dev (fed to switch_dev_register/unregister)
-    @name: no change
-    @dev: no change
-    @index: drop (not used in switch device driver side anyway)
-    @state: no change
-	If you have used @state with magic numbers, keep it
-	at this step.
-    @print_name: no change but type change (switch_dev->extcon_dev)
-    @print_state: no change but type change (switch_dev->extcon_dev)
-
-- switch_dev_register(sdev, dev)
-	=> extcon_dev_register(edev)
-	: type change (sdev->edev)
-	: remove second param('dev'). if edev has parent device, should store
-	  'dev' to 'edev.dev.parent' before registering extcon device
-- switch_dev_unregister(sdev)
-	=> extcon_dev_unregister(edev)
-	: no change but type change (sdev->edev)
-- switch_get_state(sdev)
-	=> extcon_get_state(edev)
-	: no change but type change (sdev->edev) and (return: int->u32)
-- switch_set_state(sdev, state)
-	=> extcon_set_state(edev, state)
-	: no change but type change (sdev->edev) and (state: int->u32)
-
-With this changes, the ex-switch extcon class device works as it once
-worked as switch class device. However, it will now have additional
-interfaces (both ABI and in-kernel API) and different ABI locations.
-However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
-/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
-so that they are still compatible with legacy userspace processes.
-
-****** STEP 2. Multistate (no more magic numbers in state value)
-	Extcon's extended features for switch device drivers with
-	complex features usually required magic numbers in state
-	value of switch_dev. With extcon, such magic numbers that
-	support multiple cables are no more required or supported.
-
-  1. Define cable names at edev->supported_cable.
-  2. (Recommended) remove print_state callback.
-  3. Use extcon_get_cable_state_(edev, index) or
-   extcon_get_cable_state(edev, cable_name) instead of
-   extcon_get_state(edev) if you intend to get a state of a specific
-   cable. Same for set_state. This way, you can remove the usage of
-   magic numbers in state value.
-  4. Use extcon_update_state() if you are updating specific bits of
-   the state value.
-
-Example: a switch device driver w/ magic numbers for two cables.
-	"0x00": no cables connected.
-	"0x01": cable 1 connected
-	"0x02": cable 2 connected
-	"0x03": cable 1 and 2 connected
-  1. edev->supported_cable = {"1", "2", NULL};
-  2. edev->print_state = NULL;
-  3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
-     extcon_get_cable_state(edev, "1") shows cable 1's state.
-     extcon_set_cable_state_(edev, 1) sets cable 2's state.
-     extcon_set_cable_state(edev, "2") sets cable 2's state
-  4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
-
-****** STEP 3. Notify other device drivers
-
-  You can notify others of the cable attach/detach events with
-notifier chains.
-
-  At the side of other device drivers (the extcon device itself
-does not need to get notified of its own events), there are two
-methods to register notifier_block for cable events:
-(a) for a specific cable or (b) for every cable.
-
-  (a) extcon_register_interest(obj, extcon_name, cable_name, nb)
-	Example: want to get news of "MAX8997_MUIC"'s "USB" cable
-
-	obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
-		      GFP_KERNEL);
-	nb->notifier_call = the_callback_to_handle_usb;
-
-	extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
-
-  (b) extcon_register_notifier(edev, nb)
-	Call nb for any changes in edev.
-
-  Please note that in order to properly behave with method (a),
-the extcon device driver should support multistate feature (STEP 2).
-
-****** STEP 4. Inter-cable relation (mutually exclusive)
-
-  You can provide inter-cable mutually exclusiveness information
-for an extcon device. When cables A and B are declared to be mutually
-exclusive, the two cables cannot be in ATTACHED state simulteneously.
-
-
-/*****************************************************************
- * CHAPTER 2.                                                    *
- * PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT              *
- *****************************************************************/
-
-****** ABI Location
-
-  If "CONFIG_ANDROID" is enabled, /sys/class/switch/* are created
-as symbolic links to /sys/class/extcon/*.
-
-  The two files of switch class, name and state, are provided with
-extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
-not enabled or print_state callback is supplied, the output of
-state ABI is same with switch class.

+ 4 - 0
Documentation/w1/slaves/00-INDEX

@@ -2,7 +2,11 @@
 	- This file
 	- This file
 w1_therm
 w1_therm
 	- The Maxim/Dallas Semiconductor ds18*20 temperature sensor.
 	- The Maxim/Dallas Semiconductor ds18*20 temperature sensor.
+w1_ds2413
+	- The Maxim/Dallas Semiconductor ds2413 dual channel addressable switch.
 w1_ds2423
 w1_ds2423
 	- The Maxim/Dallas Semiconductor ds2423 counter device.
 	- The Maxim/Dallas Semiconductor ds2423 counter device.
+w1_ds2438
+	- The Maxim/Dallas Semiconductor ds2438 smart battery monitor.
 w1_ds28e04
 w1_ds28e04
 	- The Maxim/Dallas Semiconductor ds28e04 eeprom.
 	- The Maxim/Dallas Semiconductor ds28e04 eeprom.

+ 50 - 0
Documentation/w1/slaves/w1_ds2413

@@ -0,0 +1,50 @@
+Kernel driver w1_ds2413
+=======================
+
+Supported chips:
+  * Maxim DS2413 1-Wire Dual Channel Addressable Switch
+
+supported family codes:
+        W1_FAMILY_DS2413        0x3A
+
+Author: Mariusz Bialonczyk <manio@skyboo.net>
+
+Description
+-----------
+
+The DS2413 chip has two open-drain outputs (PIO A and PIO B).
+Support is provided through the sysfs files "output" and "state".
+
+Reading state
+-------------
+The "state" file provides one-byte value which is in the same format as for
+the chip PIO_ACCESS_READ command (refer the datasheet for details):
+
+Bit 0:   PIOA Pin State
+Bit 1:   PIOA Output Latch State
+Bit 2:   PIOB Pin State
+Bit 3:   PIOB Output Latch State
+Bit 4-7: Complement of Bit 3 to Bit 0 (verified by the kernel module)
+
+This file is readonly.
+
+Writing output
+--------------
+You can set the PIO pins using the "output" file.
+It is writable, you can write one-byte value to this sysfs file.
+Similarly the byte format is the same as for the PIO_ACCESS_WRITE command:
+
+Bit 0:   PIOA
+Bit 1:   PIOB
+Bit 2-7: No matter (driver will set it to "1"s)
+
+
+The chip has some kind of basic protection against transmission errors.
+When reading the state, there is a four complement bits.
+The driver is checking this complement, and when it is wrong then it is
+returning I/O error.
+
+When writing output, the master must repeat the PIO Output Data byte in
+its inverted form and it is waiting for a confirmation.
+If the write is unsuccessful for three times, the write also returns
+I/O error.

+ 63 - 0
Documentation/w1/slaves/w1_ds2438

@@ -0,0 +1,63 @@
+Kernel driver w1_ds2438
+=======================
+
+Supported chips:
+  * Maxim DS2438 Smart Battery Monitor
+
+supported family codes:
+        W1_FAMILY_DS2438        0x26
+
+Author: Mariusz Bialonczyk <manio@skyboo.net>
+
+Description
+-----------
+
+The DS2438 chip provides several functions that are desirable to carry in
+a battery pack. It also has a 40 bytes of nonvolatile EEPROM.
+Because the ability of temperature, current and voltage measurement, the chip
+is also often used in weather stations and applications such as: rain gauge,
+wind speed/direction measuring, humidity sensing, etc.
+
+Current support is provided through the following sysfs files (all files
+except "iad" are readonly):
+
+"iad"
+-----
+This file controls the 'Current A/D Control Bit' (IAD) in the
+Status/Configuration Register.
+Writing a zero value will clear the IAD bit and disables the current
+measurements.
+Writing value "1" is setting the IAD bit (enables the measurements).
+The IAD bit is enabled by default in the DS2438.
+
+When writing to sysfs file bits 2-7 are ignored, so it's safe to write ASCII.
+An I/O error is returned when there is a problem setting the new value.
+
+"page0"
+-------
+This file provides full 8 bytes of the chip Page 0 (00h).
+This page contains the most frequently accessed information of the DS2438.
+Internally when this file is read, the additional CRC byte is also obtained
+from the slave device. If it is correct, the 8 bytes page data are passed
+to userspace, otherwise an I/O error is returned.
+
+"temperature"
+-------------
+Opening and reading this file initiates the CONVERT_T (temperature conversion)
+command of the chip, afterwards the temperature is read from the device
+registers and provided as an ASCII decimal value.
+
+Important: The returned value has to be divided by 256 to get a real
+temperature in degrees Celsius.
+
+"vad", "vdd"
+------------
+Opening and reading this file initiates the CONVERT_V (voltage conversion)
+command of the chip.
+
+Depending on a sysfs filename a different input for the A/D will be selected:
+vad: general purpose A/D input (VAD)
+vdd: battery input (VDD)
+
+After the voltage conversion the value is returned as decimal ASCII.
+Note: The value is in mV, so to get a volts the value has to be divided by 10.

+ 4 - 4
MAINTAINERS

@@ -5161,7 +5161,6 @@ F:	include/uapi/linux/firewire*.h
 F:	tools/firewire/
 F:	tools/firewire/
 
 
 FIRMWARE LOADER (request_firmware)
 FIRMWARE LOADER (request_firmware)
-M:	Ming Lei <ming.lei@canonical.com>
 M:	Luis R. Rodriguez <mcgrof@kernel.org>
 M:	Luis R. Rodriguez <mcgrof@kernel.org>
 L:	linux-kernel@vger.kernel.org
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 S:	Maintained
@@ -5191,13 +5190,15 @@ F:	include/linux/ipmi-fru.h
 K:	fmc_d.*register
 K:	fmc_d.*register
 
 
 FPGA MANAGER FRAMEWORK
 FPGA MANAGER FRAMEWORK
-M:	Alan Tull <atull@opensource.altera.com>
+M:	Alan Tull <atull@kernel.org>
 R:	Moritz Fischer <moritz.fischer@ettus.com>
 R:	Moritz Fischer <moritz.fischer@ettus.com>
 L:	linux-fpga@vger.kernel.org
 L:	linux-fpga@vger.kernel.org
 S:	Maintained
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
+F:	Documentation/fpga/
+F:	Documentation/devicetree/bindings/fpga/
 F:	drivers/fpga/
 F:	drivers/fpga/
-F:	include/linux/fpga/fpga-mgr.h
+F:	include/linux/fpga/
 W:	http://www.rocketboards.org
 W:	http://www.rocketboards.org
 
 
 FPU EMULATOR
 FPU EMULATOR
@@ -9131,7 +9132,6 @@ F:	drivers/nvme/target/fcloop.c
 
 
 NVMEM FRAMEWORK
 NVMEM FRAMEWORK
 M:	Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 M:	Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-M:	Maxime Ripard <maxime.ripard@free-electrons.com>
 S:	Maintained
 S:	Maintained
 F:	drivers/nvmem/
 F:	drivers/nvmem/
 F:	Documentation/devicetree/bindings/nvmem/
 F:	Documentation/devicetree/bindings/nvmem/

+ 26 - 0
arch/arm/mach-ep93xx/ts72xx.c

@@ -210,6 +210,28 @@ static struct ep93xx_eth_data __initdata ts72xx_eth_data = {
 	.phy_id		= 1,
 	.phy_id		= 1,
 };
 };
 
 
+#if IS_ENABLED(CONFIG_FPGA_MGR_TS73XX)
+
+/* Relative to EP93XX_CS1_PHYS_BASE */
+#define TS73XX_FPGA_LOADER_BASE		0x03c00000
+
+static struct resource ts73xx_fpga_resources[] = {
+	{
+		.start	= EP93XX_CS1_PHYS_BASE + TS73XX_FPGA_LOADER_BASE,
+		.end	= EP93XX_CS1_PHYS_BASE + TS73XX_FPGA_LOADER_BASE + 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ts73xx_fpga_device = {
+	.name	= "ts73xx-fpga-mgr",
+	.id	= -1,
+	.resource = ts73xx_fpga_resources,
+	.num_resources = ARRAY_SIZE(ts73xx_fpga_resources),
+};
+
+#endif
+
 static void __init ts72xx_init_machine(void)
 static void __init ts72xx_init_machine(void)
 {
 {
 	ep93xx_init_devices();
 	ep93xx_init_devices();
@@ -218,6 +240,10 @@ static void __init ts72xx_init_machine(void)
 	platform_device_register(&ts72xx_wdt_device);
 	platform_device_register(&ts72xx_wdt_device);
 
 
 	ep93xx_register_eth(&ts72xx_eth_data, 1);
 	ep93xx_register_eth(&ts72xx_eth_data, 1);
+#if IS_ENABLED(CONFIG_FPGA_MGR_TS73XX)
+	if (board_is_ts7300())
+		platform_device_register(&ts73xx_fpga_device);
+#endif
 }
 }
 
 
 MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC")
 MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC")

+ 1 - 1
arch/x86/hyperv/hv_init.c

@@ -25,7 +25,7 @@
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/clockchips.h>
 #include <linux/clockchips.h>
-
+#include <linux/hyperv.h>
 
 
 #ifdef CONFIG_HYPERV_TSCPAGE
 #ifdef CONFIG_HYPERV_TSCPAGE
 
 

+ 6 - 1
arch/x86/include/uapi/asm/hyperv.h

@@ -124,7 +124,7 @@
   * Recommend using hypercall for address space switches rather
   * Recommend using hypercall for address space switches rather
   * than MOV to CR3 instruction
   * than MOV to CR3 instruction
   */
   */
-#define HV_X64_MWAIT_RECOMMENDED		(1 << 0)
+#define HV_X64_AS_SWITCH_RECOMMENDED		(1 << 0)
 /* Recommend using hypercall for local TLB flushes rather
 /* Recommend using hypercall for local TLB flushes rather
  * than INVLPG or MOV to CR3 instructions */
  * than INVLPG or MOV to CR3 instructions */
 #define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED	(1 << 1)
 #define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED	(1 << 1)
@@ -147,6 +147,11 @@
  */
  */
 #define HV_X64_RELAXED_TIMING_RECOMMENDED	(1 << 5)
 #define HV_X64_RELAXED_TIMING_RECOMMENDED	(1 << 5)
 
 
+/*
+ * Virtual APIC support
+ */
+#define HV_X64_DEPRECATING_AEOI_RECOMMENDED	(1 << 9)
+
 /*
 /*
  * Crash notification flag.
  * Crash notification flag.
  */
  */

+ 3 - 0
arch/x86/kernel/cpu/mshyperv.c

@@ -49,6 +49,9 @@ void hyperv_vector_handler(struct pt_regs *regs)
 	if (vmbus_handler)
 	if (vmbus_handler)
 		vmbus_handler();
 		vmbus_handler();
 
 
+	if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
+		ack_APIC_irq();
+
 	exiting_irq();
 	exiting_irq();
 	set_irq_regs(old_regs);
 	set_irq_regs(old_regs);
 }
 }

+ 1 - 1
drivers/android/Kconfig

@@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC
 config ANDROID_BINDER_DEVICES
 config ANDROID_BINDER_DEVICES
 	string "Android Binder devices"
 	string "Android Binder devices"
 	depends on ANDROID_BINDER_IPC
 	depends on ANDROID_BINDER_IPC
-	default "binder"
+	default "binder,hwbinder"
 	---help---
 	---help---
 	  Default value for the binder.devices parameter.
 	  Default value for the binder.devices parameter.
 
 

+ 304 - 0
drivers/auxdisplay/Kconfig

@@ -13,8 +13,22 @@ menuconfig AUXDISPLAY
 
 
 	  If you say N, all options in this submenu will be skipped and disabled.
 	  If you say N, all options in this submenu will be skipped and disabled.
 
 
+config CHARLCD
+	tristate "Character LCD core support" if COMPILE_TEST
+
 if AUXDISPLAY
 if AUXDISPLAY
 
 
+config HD44780
+	tristate "HD44780 Character LCD support"
+	depends on GPIOLIB || COMPILE_TEST
+	select CHARLCD
+	---help---
+	  Enable support for Character LCDs using a HD44780 controller.
+	  The LCD is accessible through the /dev/lcd char device (10, 156).
+	  This code can either be compiled as a module, or linked into the
+	  kernel and started at boot.
+	  If you don't understand what all this is about, say N.
+
 config KS0108
 config KS0108
 	tristate "KS0108 LCD Controller"
 	tristate "KS0108 LCD Controller"
 	depends on PARPORT_PC
 	depends on PARPORT_PC
@@ -142,3 +156,293 @@ config HT16K33
 	  LED controller driver with keyscan.
 	  LED controller driver with keyscan.
 
 
 endif # AUXDISPLAY
 endif # AUXDISPLAY
+
+config ARM_CHARLCD
+	bool "ARM Ltd. Character LCD Driver"
+	depends on PLAT_VERSATILE
+	help
+	  This is a driver for the character LCD found on the ARM Ltd.
+	  Versatile and RealView Platform Baseboards. It doesn't do
+	  very much more than display the text "ARM Linux" on the first
+	  line and the Linux version on the second line, but that's
+	  still useful.
+
+config PANEL
+	tristate "Parallel port LCD/Keypad Panel support"
+	depends on PARPORT
+	select CHARLCD
+	---help---
+	  Say Y here if you have an HD44780 or KS-0074 LCD connected to your
+	  parallel port. This driver also features 4 and 6-key keypads. The LCD
+	  is accessible through the /dev/lcd char device (10, 156), and the
+	  keypad through /dev/keypad (10, 185). This code can either be
+	  compiled as a module, or linked into the kernel and started at boot.
+	  If you don't understand what all this is about, say N.
+
+if PANEL
+
+config PANEL_PARPORT
+	int "Default parallel port number (0=LPT1)"
+	range 0 255
+	default "0"
+	---help---
+	  This is the index of the parallel port the panel is connected to. One
+	  driver instance only supports one parallel port, so if your keypad
+	  and LCD are connected to two separate ports, you have to start two
+	  modules with different arguments. Numbering starts with '0' for LPT1,
+	  and so on.
+
+config PANEL_PROFILE
+	int "Default panel profile (0-5, 0=custom)"
+	range 0 5
+	default "5"
+	---help---
+	  To ease configuration, the driver supports different configuration
+	  profiles for past and recent wirings. These profiles can also be
+	  used to define an approximative configuration, completed by a few
+	  other options. Here are the profiles :
+
+	    0 = custom (see further)
+	    1 = 2x16 parallel LCD, old keypad
+	    2 = 2x16 serial LCD (KS-0074), new keypad
+	    3 = 2x16 parallel LCD (Hantronix), no keypad
+	    4 = 2x16 parallel LCD (Nexcom NSA1045) with Nexcom's keypad
+	    5 = 2x40 parallel LCD (old one), with old keypad
+
+	  Custom configurations allow you to define how your display is
+	  wired to the parallel port, and how it works. This is only intended
+	  for experts.
+
+config PANEL_KEYPAD
+	depends on PANEL_PROFILE="0"
+	int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)"
+	range 0 3
+	default 0
+	---help---
+	  This enables and configures a keypad connected to the parallel port.
+	  The keys will be read from character device 10,185. Valid values are :
+
+	    0 : do not enable this driver
+	    1 : old 6 keys keypad
+	    2 : new 6 keys keypad, as used on the server at www.ant-computing.com
+	    3 : Nexcom NSA1045's 4 keys keypad
+
+	  New profiles can be described in the driver source. The driver also
+	  supports simultaneous keys pressed when the keypad supports them.
+
+config PANEL_LCD
+	depends on PANEL_PROFILE="0"
+	int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)"
+	range 0 5
+	default 0
+	---help---
+	   This enables and configures an LCD connected to the parallel port.
+	   The driver includes an interpreter for escape codes starting with
+	   '\e[L' which are specific to the LCD, and a few ANSI codes. The
+	   driver will be registered as character device 10,156, usually
+	   under the name '/dev/lcd'. There are a total of 6 supported types :
+
+	     0 : do not enable the driver
+	     1 : custom configuration and wiring (see further)
+	     2 : 2x16 & 2x40 parallel LCD (old wiring)
+	     3 : 2x16 serial LCD (KS-0074 based)
+	     4 : 2x16 parallel LCD (Hantronix wiring)
+	     5 : 2x16 parallel LCD (Nexcom wiring)
+
+	   When type '1' is specified, other options will appear to configure
+	   more precise aspects (wiring, dimensions, protocol, ...). Please note
+	   that those values changed from the 2.4 driver for better consistency.
+
+config PANEL_LCD_HEIGHT
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "Number of lines on the LCD (1-2)"
+	range 1 2
+	default 2
+	---help---
+	  This is the number of visible character lines on the LCD in custom profile.
+	  It can either be 1 or 2.
+
+config PANEL_LCD_WIDTH
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "Number of characters per line on the LCD (1-40)"
+	range 1 40
+	default 40
+	---help---
+	  This is the number of characters per line on the LCD in custom profile.
+	  Common values are 16,20,24,40.
+
+config PANEL_LCD_BWIDTH
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "Internal LCD line width (1-40, 40 by default)"
+	range 1 40
+	default 40
+	---help---
+	  Most LCDs use a standard controller which supports hardware lines of 40
+	  characters, although sometimes only 16, 20 or 24 of them are really wired
+	  to the terminal. This results in some non-visible but addressable characters,
+	  and is the case for most parallel LCDs. Other LCDs, and some serial ones,
+	  however, use the same line width internally as what is visible. The KS0074
+	  for example, uses 16 characters per line for 16 visible characters per line.
+
+	  This option lets you configure the value used by your LCD in 'custom' profile.
+	  If you don't know, put '40' here.
+
+config PANEL_LCD_HWIDTH
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "Hardware LCD line width (1-64, 64 by default)"
+	range 1 64
+	default 64
+	---help---
+	  Most LCDs use a single address bit to differentiate line 0 and line 1. Since
+	  some of them need to be able to address 40 chars with the lower bits, they
+	  often use the immediately superior power of 2, which is 64, to address the
+	  next line.
+
+	  If you don't know what your LCD uses, in doubt let 16 here for a 2x16, and
+	  64 here for a 2x40.
+
+config PANEL_LCD_CHARSET
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "LCD character set (0=normal, 1=KS0074)"
+	range 0 1
+	default 0
+	---help---
+	  Some controllers such as the KS0074 use a somewhat strange character set
+	  where many symbols are at unusual places. The driver knows how to map
+	  'standard' ASCII characters to the character sets used by these controllers.
+	  Valid values are :
+
+	     0 : normal (untranslated) character set
+	     1 : KS0074 character set
+
+	  If you don't know, use the normal one (0).
+
+config PANEL_LCD_PROTO
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+	int "LCD communication mode (0=parallel 8 bits, 1=serial)"
+	range 0 1
+	default 0
+	---help---
+	  This driver now supports any serial or parallel LCD wired to a parallel
+	  port. But before assigning signals, the driver needs to know if it will
+	  be driving a serial LCD or a parallel one. Serial LCDs only use 2 wires
+	  (SDA/SCL), while parallel ones use 2 or 3 wires for the control signals
+	  (E, RS, sometimes RW), and 4 or 8 for the data. Use 0 here for a 8 bits
+	  parallel LCD, and 1 for a serial LCD.
+
+config PANEL_LCD_PIN_E
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+        int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) "
+	range -17 17
+	default 14
+	---help---
+	  This describes the number of the parallel port pin to which the LCD 'E'
+	  signal has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'E' pin in custom profile is '14' (AUTOFEED).
+
+config PANEL_LCD_PIN_RS
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+        int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) "
+	range -17 17
+	default 17
+	---help---
+	  This describes the number of the parallel port pin to which the LCD 'RS'
+	  signal has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'RS' pin in custom profile is '17' (SELECT IN).
+
+config PANEL_LCD_PIN_RW
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+        int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) "
+	range -17 17
+	default 16
+	---help---
+	  This describes the number of the parallel port pin to which the LCD 'RW'
+	  signal has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'RW' pin in custom profile is '16' (INIT).
+
+config PANEL_LCD_PIN_SCL
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+        int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) "
+	range -17 17
+	default 1
+	---help---
+	  This describes the number of the parallel port pin to which the serial
+	  LCD 'SCL' signal has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'SCL' pin in custom profile is '1' (STROBE).
+
+config PANEL_LCD_PIN_SDA
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+        int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) "
+	range -17 17
+	default 2
+	---help---
+	  This describes the number of the parallel port pin to which the serial
+	  LCD 'SDA' signal has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'SDA' pin in custom profile is '2' (D0).
+
+config PANEL_LCD_PIN_BL
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
+        int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) "
+	range -17 17
+	default 0
+	---help---
+	  This describes the number of the parallel port pin to which the LCD 'BL' signal
+          has been connected. It can be :
+
+	          0 : no connection (eg: connected to ground)
+	      1..17 : directly connected to any of these pins on the DB25 plug
+	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+	  Default for the 'BL' pin in custom profile is '0' (uncontrolled).
+
+config PANEL_CHANGE_MESSAGE
+	bool "Change LCD initialization message ?"
+	default "n"
+	---help---
+	  This allows you to replace the boot message indicating the kernel version
+	  and the driver version with a custom message. This is useful on appliances
+	  where a simple 'Starting system' message can be enough to stop a customer
+	  from worrying.
+
+	  If you say 'Y' here, you'll be able to choose a message yourself. Otherwise,
+	  say 'N' and keep the default message with the version.
+
+config PANEL_BOOT_MESSAGE
+	depends on PANEL_CHANGE_MESSAGE="y"
+	string "New initialization message"
+	default ""
+	---help---
+	  This allows you to replace the boot message indicating the kernel version
+	  and the driver version with a custom message. This is useful on appliances
+	  where a simple 'Starting system' message can be enough to stop a customer
+	  from worrying.
+
+	  An empty message will only clear the display at driver init time. Any other
+	  printf()-formatted message is valid with newline and escape codes.
+
+endif # PANEL

+ 4 - 0
drivers/auxdisplay/Makefile

@@ -2,7 +2,11 @@
 # Makefile for the kernel auxiliary displays device drivers.
 # Makefile for the kernel auxiliary displays device drivers.
 #
 #
 
 
+obj-$(CONFIG_CHARLCD)		+= charlcd.o
+obj-$(CONFIG_ARM_CHARLCD)	+= arm-charlcd.o
 obj-$(CONFIG_KS0108)		+= ks0108.o
 obj-$(CONFIG_KS0108)		+= ks0108.o
 obj-$(CONFIG_CFAG12864B)	+= cfag12864b.o cfag12864bfb.o
 obj-$(CONFIG_CFAG12864B)	+= cfag12864b.o cfag12864bfb.o
 obj-$(CONFIG_IMG_ASCII_LCD)	+= img-ascii-lcd.o
 obj-$(CONFIG_IMG_ASCII_LCD)	+= img-ascii-lcd.o
+obj-$(CONFIG_HD44780)		+= hd44780.o
 obj-$(CONFIG_HT16K33)		+= ht16k33.o
 obj-$(CONFIG_HT16K33)		+= ht16k33.o
+obj-$(CONFIG_PANEL)             += panel.o

+ 0 - 0
drivers/misc/arm-charlcd.c → drivers/auxdisplay/arm-charlcd.c


+ 818 - 0
drivers/auxdisplay/charlcd.c

@@ -0,0 +1,818 @@
+/*
+ * Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+
+#include <generated/utsrelease.h>
+
+#include <misc/charlcd.h>
+
+#define LCD_MINOR		156
+
+#define DEFAULT_LCD_BWIDTH      40
+#define DEFAULT_LCD_HWIDTH      64
+
+/* Keep the backlight on this many seconds for each flash */
+#define LCD_BL_TEMPO_PERIOD	4
+
+#define LCD_FLAG_B		0x0004	/* Blink on */
+#define LCD_FLAG_C		0x0008	/* Cursor on */
+#define LCD_FLAG_D		0x0010	/* Display on */
+#define LCD_FLAG_F		0x0020	/* Large font mode */
+#define LCD_FLAG_N		0x0040	/* 2-rows mode */
+#define LCD_FLAG_L		0x0080	/* Backlight enabled */
+
+/* LCD commands */
+#define LCD_CMD_DISPLAY_CLEAR	0x01	/* Clear entire display */
+
+#define LCD_CMD_ENTRY_MODE	0x04	/* Set entry mode */
+#define LCD_CMD_CURSOR_INC	0x02	/* Increment cursor */
+
+#define LCD_CMD_DISPLAY_CTRL	0x08	/* Display control */
+#define LCD_CMD_DISPLAY_ON	0x04	/* Set display on */
+#define LCD_CMD_CURSOR_ON	0x02	/* Set cursor on */
+#define LCD_CMD_BLINK_ON	0x01	/* Set blink on */
+
+#define LCD_CMD_SHIFT		0x10	/* Shift cursor/display */
+#define LCD_CMD_DISPLAY_SHIFT	0x08	/* Shift display instead of cursor */
+#define LCD_CMD_SHIFT_RIGHT	0x04	/* Shift display/cursor to the right */
+
+#define LCD_CMD_FUNCTION_SET	0x20	/* Set function */
+#define LCD_CMD_DATA_LEN_8BITS	0x10	/* Set data length to 8 bits */
+#define LCD_CMD_TWO_LINES	0x08	/* Set to two display lines */
+#define LCD_CMD_FONT_5X10_DOTS	0x04	/* Set char font to 5x10 dots */
+
+#define LCD_CMD_SET_CGRAM_ADDR	0x40	/* Set char generator RAM address */
+
+#define LCD_CMD_SET_DDRAM_ADDR	0x80	/* Set display data RAM address */
+
+#define LCD_ESCAPE_LEN		24	/* Max chars for LCD escape command */
+#define LCD_ESCAPE_CHAR		27	/* Use char 27 for escape command */
+
+struct charlcd_priv {
+	struct charlcd lcd;
+
+	struct delayed_work bl_work;
+	struct mutex bl_tempo_lock;	/* Protects access to bl_tempo */
+	bool bl_tempo;
+
+	bool must_clear;
+
+	/* contains the LCD config state */
+	unsigned long int flags;
+
+	/* Contains the LCD X and Y offset */
+	struct {
+		unsigned long int x;
+		unsigned long int y;
+	} addr;
+
+	/* Current escape sequence and it's length or -1 if outside */
+	struct {
+		char buf[LCD_ESCAPE_LEN + 1];
+		int len;
+	} esc_seq;
+
+	unsigned long long drvdata[0];
+};
+
+#define to_priv(p)	container_of(p, struct charlcd_priv, lcd)
+
+/* Device single-open policy control */
+static atomic_t charlcd_available = ATOMIC_INIT(1);
+
+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+	if (in_interrupt())
+		mdelay(ms);
+	else
+		schedule_timeout_interruptible(msecs_to_jiffies(ms));
+}
+
+/* turn the backlight on or off */
+static void charlcd_backlight(struct charlcd *lcd, int on)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (!lcd->ops->backlight)
+		return;
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (!priv->bl_tempo)
+		lcd->ops->backlight(lcd, on);
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+
+static void charlcd_bl_off(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct charlcd_priv *priv =
+		container_of(dwork, struct charlcd_priv, bl_work);
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (priv->bl_tempo) {
+		priv->bl_tempo = false;
+		if (!(priv->flags & LCD_FLAG_L))
+			priv->lcd.ops->backlight(&priv->lcd, 0);
+	}
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+
+/* turn the backlight on for a little while */
+void charlcd_poke(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (!lcd->ops->backlight)
+		return;
+
+	cancel_delayed_work_sync(&priv->bl_work);
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
+		lcd->ops->backlight(lcd, 1);
+	priv->bl_tempo = true;
+	schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+EXPORT_SYMBOL_GPL(charlcd_poke);
+
+static void charlcd_gotoxy(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+	unsigned int addr;
+
+	/*
+	 * we force the cursor to stay at the end of the
+	 * line if it wants to go farther
+	 */
+	addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1)
+					  : lcd->bwidth - 1;
+	if (priv->addr.y & 1)
+		addr += lcd->hwidth;
+	if (priv->addr.y & 2)
+		addr += lcd->bwidth;
+	lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
+}
+
+static void charlcd_home(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	priv->addr.x = 0;
+	priv->addr.y = 0;
+	charlcd_gotoxy(lcd);
+}
+
+static void charlcd_print(struct charlcd *lcd, char c)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (priv->addr.x < lcd->bwidth) {
+		if (lcd->char_conv)
+			c = lcd->char_conv[(unsigned char)c];
+		lcd->ops->write_data(lcd, c);
+		priv->addr.x++;
+	}
+	/* prevents the cursor from wrapping onto the next line */
+	if (priv->addr.x == lcd->bwidth)
+		charlcd_gotoxy(lcd);
+}
+
+static void charlcd_clear_fast(struct charlcd *lcd)
+{
+	int pos;
+
+	charlcd_home(lcd);
+
+	if (lcd->ops->clear_fast)
+		lcd->ops->clear_fast(lcd);
+	else
+		for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
+			lcd->ops->write_data(lcd, ' ');
+
+	charlcd_home(lcd);
+}
+
+/* clears the display and resets X/Y */
+static void charlcd_clear_display(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
+	priv->addr.x = 0;
+	priv->addr.y = 0;
+	/* we must wait a few milliseconds (15) */
+	long_sleep(15);
+}
+
+static int charlcd_init_display(struct charlcd *lcd)
+{
+	void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
+	struct charlcd_priv *priv = to_priv(lcd);
+	u8 init;
+
+	if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
+		return -EINVAL;
+
+	priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+		      LCD_FLAG_C | LCD_FLAG_B;
+
+	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
+
+	/*
+	 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
+	 * the LCD is in 8-bit mode afterwards
+	 */
+	init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
+	if (lcd->ifwidth == 4) {
+		init >>= 4;
+		write_cmd_raw = lcd->ops->write_cmd_raw4;
+	} else {
+		write_cmd_raw = lcd->ops->write_cmd;
+	}
+	write_cmd_raw(lcd, init);
+	long_sleep(10);
+	write_cmd_raw(lcd, init);
+	long_sleep(10);
+	write_cmd_raw(lcd, init);
+	long_sleep(10);
+
+	if (lcd->ifwidth == 4) {
+		/* Switch to 4-bit mode, 1 line, small fonts */
+		lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
+		long_sleep(10);
+	}
+
+	/* set font height and lines number */
+	lcd->ops->write_cmd(lcd,
+		LCD_CMD_FUNCTION_SET |
+		((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+		((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+		((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+	long_sleep(10);
+
+	/* display off, cursor off, blink off */
+	lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
+	long_sleep(10);
+
+	lcd->ops->write_cmd(lcd,
+		LCD_CMD_DISPLAY_CTRL |	/* set display mode */
+		((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+		((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+		((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+
+	charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
+
+	long_sleep(10);
+
+	/* entry mode set : increment, cursor shifting */
+	lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+	charlcd_clear_display(lcd);
+	return 0;
+}
+
+/*
+ * These are the file operation function for user access to /dev/lcd
+ * This function can also be called from inside the kernel, by
+ * setting file and ppos to NULL.
+ *
+ */
+
+static inline int handle_lcd_special_code(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	/* LCD special codes */
+
+	int processed = 0;
+
+	char *esc = priv->esc_seq.buf + 2;
+	int oldflags = priv->flags;
+
+	/* check for display mode flags */
+	switch (*esc) {
+	case 'D':	/* Display ON */
+		priv->flags |= LCD_FLAG_D;
+		processed = 1;
+		break;
+	case 'd':	/* Display OFF */
+		priv->flags &= ~LCD_FLAG_D;
+		processed = 1;
+		break;
+	case 'C':	/* Cursor ON */
+		priv->flags |= LCD_FLAG_C;
+		processed = 1;
+		break;
+	case 'c':	/* Cursor OFF */
+		priv->flags &= ~LCD_FLAG_C;
+		processed = 1;
+		break;
+	case 'B':	/* Blink ON */
+		priv->flags |= LCD_FLAG_B;
+		processed = 1;
+		break;
+	case 'b':	/* Blink OFF */
+		priv->flags &= ~LCD_FLAG_B;
+		processed = 1;
+		break;
+	case '+':	/* Back light ON */
+		priv->flags |= LCD_FLAG_L;
+		processed = 1;
+		break;
+	case '-':	/* Back light OFF */
+		priv->flags &= ~LCD_FLAG_L;
+		processed = 1;
+		break;
+	case '*':	/* Flash back light */
+		charlcd_poke(lcd);
+		processed = 1;
+		break;
+	case 'f':	/* Small Font */
+		priv->flags &= ~LCD_FLAG_F;
+		processed = 1;
+		break;
+	case 'F':	/* Large Font */
+		priv->flags |= LCD_FLAG_F;
+		processed = 1;
+		break;
+	case 'n':	/* One Line */
+		priv->flags &= ~LCD_FLAG_N;
+		processed = 1;
+		break;
+	case 'N':	/* Two Lines */
+		priv->flags |= LCD_FLAG_N;
+		break;
+	case 'l':	/* Shift Cursor Left */
+		if (priv->addr.x > 0) {
+			/* back one char if not at end of line */
+			if (priv->addr.x < lcd->bwidth)
+				lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+			priv->addr.x--;
+		}
+		processed = 1;
+		break;
+	case 'r':	/* shift cursor right */
+		if (priv->addr.x < lcd->width) {
+			/* allow the cursor to pass the end of the line */
+			if (priv->addr.x < (lcd->bwidth - 1))
+				lcd->ops->write_cmd(lcd,
+					LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
+			priv->addr.x++;
+		}
+		processed = 1;
+		break;
+	case 'L':	/* shift display left */
+		lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+		processed = 1;
+		break;
+	case 'R':	/* shift display right */
+		lcd->ops->write_cmd(lcd,
+				    LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+				    LCD_CMD_SHIFT_RIGHT);
+		processed = 1;
+		break;
+	case 'k': {	/* kill end of line */
+		int x;
+
+		for (x = priv->addr.x; x < lcd->bwidth; x++)
+			lcd->ops->write_data(lcd, ' ');
+
+		/* restore cursor position */
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+	case 'I':	/* reinitialize display */
+		charlcd_init_display(lcd);
+		processed = 1;
+		break;
+	case 'G': {
+		/* Generator : LGcxxxxx...xx; must have <c> between '0'
+		 * and '7', representing the numerical ASCII code of the
+		 * redefined character, and <xx...xx> a sequence of 16
+		 * hex digits representing 8 bytes for each character.
+		 * Most LCDs will only use 5 lower bits of the 7 first
+		 * bytes.
+		 */
+
+		unsigned char cgbytes[8];
+		unsigned char cgaddr;
+		int cgoffset;
+		int shift;
+		char value;
+		int addr;
+
+		if (!strchr(esc, ';'))
+			break;
+
+		esc++;
+
+		cgaddr = *(esc++) - '0';
+		if (cgaddr > 7) {
+			processed = 1;
+			break;
+		}
+
+		cgoffset = 0;
+		shift = 0;
+		value = 0;
+		while (*esc && cgoffset < 8) {
+			shift ^= 4;
+			if (*esc >= '0' && *esc <= '9') {
+				value |= (*esc - '0') << shift;
+			} else if (*esc >= 'A' && *esc <= 'Z') {
+				value |= (*esc - 'A' + 10) << shift;
+			} else if (*esc >= 'a' && *esc <= 'z') {
+				value |= (*esc - 'a' + 10) << shift;
+			} else {
+				esc++;
+				continue;
+			}
+
+			if (shift == 0) {
+				cgbytes[cgoffset++] = value;
+				value = 0;
+			}
+
+			esc++;
+		}
+
+		lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+		for (addr = 0; addr < cgoffset; addr++)
+			lcd->ops->write_data(lcd, cgbytes[addr]);
+
+		/* ensures that we stop writing to CGRAM */
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+	case 'x':	/* gotoxy : LxXXX[yYYY]; */
+	case 'y':	/* gotoxy : LyYYY[xXXX]; */
+		if (!strchr(esc, ';'))
+			break;
+
+		while (*esc) {
+			if (*esc == 'x') {
+				esc++;
+				if (kstrtoul(esc, 10, &priv->addr.x) < 0)
+					break;
+			} else if (*esc == 'y') {
+				esc++;
+				if (kstrtoul(esc, 10, &priv->addr.y) < 0)
+					break;
+			} else {
+				break;
+			}
+		}
+
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+
+	/* TODO: This indent party here got ugly, clean it! */
+	/* Check whether one flag was changed */
+	if (oldflags == priv->flags)
+		return processed;
+
+	/* check whether one of B,C,D flags were changed */
+	if ((oldflags ^ priv->flags) &
+	    (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
+		/* set display mode */
+		lcd->ops->write_cmd(lcd,
+			LCD_CMD_DISPLAY_CTRL |
+			((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+			((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+			((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+	/* check whether one of F,N flags was changed */
+	else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
+		lcd->ops->write_cmd(lcd,
+			LCD_CMD_FUNCTION_SET |
+			((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+			((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+			((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+	/* check whether L flag was changed */
+	else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
+		charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
+
+	return processed;
+}
+
+static void charlcd_write_char(struct charlcd *lcd, char c)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	/* first, we'll test if we're in escape mode */
+	if ((c != '\n') && priv->esc_seq.len >= 0) {
+		/* yes, let's add this char to the buffer */
+		priv->esc_seq.buf[priv->esc_seq.len++] = c;
+		priv->esc_seq.buf[priv->esc_seq.len] = 0;
+	} else {
+		/* aborts any previous escape sequence */
+		priv->esc_seq.len = -1;
+
+		switch (c) {
+		case LCD_ESCAPE_CHAR:
+			/* start of an escape sequence */
+			priv->esc_seq.len = 0;
+			priv->esc_seq.buf[priv->esc_seq.len] = 0;
+			break;
+		case '\b':
+			/* go back one char and clear it */
+			if (priv->addr.x > 0) {
+				/*
+				 * check if we're not at the
+				 * end of the line
+				 */
+				if (priv->addr.x < lcd->bwidth)
+					/* back one char */
+					lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+				priv->addr.x--;
+			}
+			/* replace with a space */
+			lcd->ops->write_data(lcd, ' ');
+			/* back one char again */
+			lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+			break;
+		case '\014':
+			/* quickly clear the display */
+			charlcd_clear_fast(lcd);
+			break;
+		case '\n':
+			/*
+			 * flush the remainder of the current line and
+			 * go to the beginning of the next line
+			 */
+			for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
+				lcd->ops->write_data(lcd, ' ');
+			priv->addr.x = 0;
+			priv->addr.y = (priv->addr.y + 1) % lcd->height;
+			charlcd_gotoxy(lcd);
+			break;
+		case '\r':
+			/* go to the beginning of the same line */
+			priv->addr.x = 0;
+			charlcd_gotoxy(lcd);
+			break;
+		case '\t':
+			/* print a space instead of the tab */
+			charlcd_print(lcd, ' ');
+			break;
+		default:
+			/* simply print this char */
+			charlcd_print(lcd, c);
+			break;
+		}
+	}
+
+	/*
+	 * now we'll see if we're in an escape mode and if the current
+	 * escape sequence can be understood.
+	 */
+	if (priv->esc_seq.len >= 2) {
+		int processed = 0;
+
+		if (!strcmp(priv->esc_seq.buf, "[2J")) {
+			/* clear the display */
+			charlcd_clear_fast(lcd);
+			processed = 1;
+		} else if (!strcmp(priv->esc_seq.buf, "[H")) {
+			/* cursor to home */
+			charlcd_home(lcd);
+			processed = 1;
+		}
+		/* codes starting with ^[[L */
+		else if ((priv->esc_seq.len >= 3) &&
+			 (priv->esc_seq.buf[0] == '[') &&
+			 (priv->esc_seq.buf[1] == 'L')) {
+			processed = handle_lcd_special_code(lcd);
+		}
+
+		/* LCD special escape codes */
+		/*
+		 * flush the escape sequence if it's been processed
+		 * or if it is getting too long.
+		 */
+		if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
+			priv->esc_seq.len = -1;
+	} /* escape codes */
+}
+
+static struct charlcd *the_charlcd;
+
+static ssize_t charlcd_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	const char __user *tmp = buf;
+	char c;
+
+	for (; count-- > 0; (*ppos)++, tmp++) {
+		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+			/*
+			 * let's be a little nice with other processes
+			 * that need some CPU
+			 */
+			schedule();
+
+		if (get_user(c, tmp))
+			return -EFAULT;
+
+		charlcd_write_char(the_charlcd, c);
+	}
+
+	return tmp - buf;
+}
+
+static int charlcd_open(struct inode *inode, struct file *file)
+{
+	struct charlcd_priv *priv = to_priv(the_charlcd);
+
+	if (!atomic_dec_and_test(&charlcd_available))
+		return -EBUSY;	/* open only once at a time */
+
+	if (file->f_mode & FMODE_READ)	/* device is write-only */
+		return -EPERM;
+
+	if (priv->must_clear) {
+		charlcd_clear_display(&priv->lcd);
+		priv->must_clear = false;
+	}
+	return nonseekable_open(inode, file);
+}
+
+static int charlcd_release(struct inode *inode, struct file *file)
+{
+	atomic_inc(&charlcd_available);
+	return 0;
+}
+
+static const struct file_operations charlcd_fops = {
+	.write   = charlcd_write,
+	.open    = charlcd_open,
+	.release = charlcd_release,
+	.llseek  = no_llseek,
+};
+
+static struct miscdevice charlcd_dev = {
+	.minor	= LCD_MINOR,
+	.name	= "lcd",
+	.fops	= &charlcd_fops,
+};
+
+static void charlcd_puts(struct charlcd *lcd, const char *s)
+{
+	const char *tmp = s;
+	int count = strlen(s);
+
+	for (; count-- > 0; tmp++) {
+		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+			/*
+			 * let's be a little nice with other processes
+			 * that need some CPU
+			 */
+			schedule();
+
+		charlcd_write_char(lcd, *tmp);
+	}
+}
+
+/* initialize the LCD driver */
+static int charlcd_init(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+	int ret;
+
+	if (lcd->ops->backlight) {
+		mutex_init(&priv->bl_tempo_lock);
+		INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
+	}
+
+	/*
+	 * before this line, we must NOT send anything to the display.
+	 * Since charlcd_init_display() needs to write data, we have to
+	 * enable mark the LCD initialized just before.
+	 */
+	ret = charlcd_init_display(lcd);
+	if (ret)
+		return ret;
+
+	/* display a short message */
+#ifdef CONFIG_PANEL_CHANGE_MESSAGE
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+	charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
+#endif
+#else
+	charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\n");
+#endif
+	/* clear the display on the next device opening */
+	priv->must_clear = true;
+	charlcd_home(lcd);
+	return 0;
+}
+
+struct charlcd *charlcd_alloc(unsigned int drvdata_size)
+{
+	struct charlcd_priv *priv;
+	struct charlcd *lcd;
+
+	priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->esc_seq.len = -1;
+
+	lcd = &priv->lcd;
+	lcd->ifwidth = 8;
+	lcd->bwidth = DEFAULT_LCD_BWIDTH;
+	lcd->hwidth = DEFAULT_LCD_HWIDTH;
+	lcd->drvdata = priv->drvdata;
+
+	return lcd;
+}
+EXPORT_SYMBOL_GPL(charlcd_alloc);
+
+static int panel_notify_sys(struct notifier_block *this, unsigned long code,
+			    void *unused)
+{
+	struct charlcd *lcd = the_charlcd;
+
+	switch (code) {
+	case SYS_DOWN:
+		charlcd_puts(lcd,
+			     "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	case SYS_HALT:
+		charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	case SYS_POWER_OFF:
+		charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panel_notifier = {
+	panel_notify_sys,
+	NULL,
+	0
+};
+
+int charlcd_register(struct charlcd *lcd)
+{
+	int ret;
+
+	ret = charlcd_init(lcd);
+	if (ret)
+		return ret;
+
+	ret = misc_register(&charlcd_dev);
+	if (ret)
+		return ret;
+
+	the_charlcd = lcd;
+	register_reboot_notifier(&panel_notifier);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_register);
+
+int charlcd_unregister(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	unregister_reboot_notifier(&panel_notifier);
+	charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+	misc_deregister(&charlcd_dev);
+	the_charlcd = NULL;
+	if (lcd->ops->backlight) {
+		cancel_delayed_work_sync(&priv->bl_work);
+		priv->lcd.ops->backlight(&priv->lcd, 0);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_unregister);
+
+MODULE_LICENSE("GPL");

+ 326 - 0
drivers/auxdisplay/hd44780.c

@@ -0,0 +1,326 @@
+/*
+ * HD44780 Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include <misc/charlcd.h>
+
+
+enum hd44780_pin {
+	/* Order does matter due to writing to GPIO array subsets! */
+	PIN_DATA0,	/* Optional */
+	PIN_DATA1,	/* Optional */
+	PIN_DATA2,	/* Optional */
+	PIN_DATA3,	/* Optional */
+	PIN_DATA4,
+	PIN_DATA5,
+	PIN_DATA6,
+	PIN_DATA7,
+	PIN_CTRL_RS,
+	PIN_CTRL_RW,	/* Optional */
+	PIN_CTRL_E,
+	PIN_CTRL_BL,   /* Optional */
+	PIN_NUM
+};
+
+struct hd44780 {
+	struct gpio_desc *pins[PIN_NUM];
+};
+
+static void hd44780_backlight(struct charlcd *lcd, int on)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	if (hd->pins[PIN_CTRL_BL])
+		gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
+}
+
+static void hd44780_strobe_gpio(struct hd44780 *hd)
+{
+	/* Maintain the data during 20 us before the strobe */
+	udelay(20);
+
+	gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 1);
+
+	/* Maintain the strobe during 40 us */
+	udelay(40);
+
+	gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 0);
+}
+
+/* write to an LCD panel register in 8 bit GPIO mode */
+static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+	int values[10];	/* for DATA[0-7], RS, RW */
+	unsigned int i, n;
+
+	for (i = 0; i < 8; i++)
+		values[PIN_DATA0 + i] = !!(val & BIT(i));
+	values[PIN_CTRL_RS] = rs;
+	n = 9;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* write to an LCD panel register in 4 bit GPIO mode */
+static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+	int values[10];	/* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+	unsigned int i, n;
+
+	/* High nibble + RS, RW */
+	for (i = 4; i < 8; i++)
+		values[PIN_DATA0 + i] = !!(val & BIT(i));
+	values[PIN_CTRL_RS] = rs;
+	n = 5;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+
+	/* Low nibble */
+	for (i = 0; i < 4; i++)
+		values[PIN_DATA4 + i] = !!(val & BIT(i));
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* Send a command to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio8(hd, cmd, 0);
+
+	/* The shortest command takes at least 120 us */
+	udelay(120);
+}
+
+/* Send data to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio8(hd, data, 1);
+
+	/* The shortest data takes at least 45 us */
+	udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio8 = {
+	.write_cmd	= hd44780_write_cmd_gpio8,
+	.write_data	= hd44780_write_data_gpio8,
+	.backlight	= hd44780_backlight,
+};
+
+/* Send a command to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio4(hd, cmd, 0);
+
+	/* The shortest command takes at least 120 us */
+	udelay(120);
+}
+
+/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
+static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
+{
+	int values[10];	/* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+	struct hd44780 *hd = lcd->drvdata;
+	unsigned int i, n;
+
+	/* Command nibble + RS, RW */
+	for (i = 0; i < 4; i++)
+		values[PIN_DATA4 + i] = !!(cmd & BIT(i));
+	values[PIN_CTRL_RS] = 0;
+	n = 5;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* Send data to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio4(hd, data, 1);
+
+	/* The shortest data takes at least 45 us */
+	udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio4 = {
+	.write_cmd	= hd44780_write_cmd_gpio4,
+	.write_cmd_raw4	= hd44780_write_cmd_raw_gpio4,
+	.write_data	= hd44780_write_data_gpio4,
+	.backlight	= hd44780_backlight,
+};
+
+static int hd44780_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	unsigned int i, base;
+	struct charlcd *lcd;
+	struct hd44780 *hd;
+	int ifwidth, ret;
+
+	/* Required pins */
+	ifwidth = gpiod_count(dev, "data");
+	if (ifwidth < 0)
+		return ifwidth;
+
+	switch (ifwidth) {
+	case 4:
+		base = PIN_DATA4;
+		break;
+	case 8:
+		base = PIN_DATA0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	lcd = charlcd_alloc(sizeof(struct hd44780));
+	if (!lcd)
+		return -ENOMEM;
+
+	hd = lcd->drvdata;
+
+	for (i = 0; i < ifwidth; i++) {
+		hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
+							  GPIOD_OUT_LOW);
+		if (IS_ERR(hd->pins[base + i])) {
+			ret = PTR_ERR(hd->pins[base + i]);
+			goto fail;
+		}
+	}
+
+	hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_E])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_E]);
+		goto fail;
+	}
+
+	hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);
+	if (IS_ERR(hd->pins[PIN_CTRL_RS])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);
+		goto fail;
+	}
+
+	/* Optional pins */
+	hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, "rw",
+							GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
+		goto fail;
+	}
+
+	hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",
+							GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_BL])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);
+		goto fail;
+	}
+
+	/* Required properties */
+	ret = device_property_read_u32(dev, "display-height-chars",
+				       &lcd->height);
+	if (ret)
+		goto fail;
+	ret = device_property_read_u32(dev, "display-width-chars", &lcd->width);
+	if (ret)
+		goto fail;
+
+	/*
+	 * On displays with more than two rows, the internal buffer width is
+	 * usually equal to the display width
+	 */
+	if (lcd->height > 2)
+		lcd->bwidth = lcd->width;
+
+	/* Optional properties */
+	device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
+
+	lcd->ifwidth = ifwidth;
+	lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
+
+	ret = charlcd_register(lcd);
+	if (ret)
+		goto fail;
+
+	platform_set_drvdata(pdev, lcd);
+	return 0;
+
+fail:
+	kfree(lcd);
+	return ret;
+}
+
+static int hd44780_remove(struct platform_device *pdev)
+{
+	struct charlcd *lcd = platform_get_drvdata(pdev);
+
+	charlcd_unregister(lcd);
+	return 0;
+}
+
+static const struct of_device_id hd44780_of_match[] = {
+	{ .compatible = "hit,hd44780" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hd44780_of_match);
+
+static struct platform_driver hd44780_driver = {
+	.probe = hd44780_probe,
+	.remove = hd44780_remove,
+	.driver		= {
+		.name	= "hd44780",
+		.of_match_table = hd44780_of_match,
+	},
+};
+
+module_platform_driver(hd44780_driver);
+MODULE_DESCRIPTION("HD44780 Character LCD driver");
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");

+ 15 - 5
drivers/auxdisplay/ht16k33.c

@@ -254,18 +254,22 @@ static bool ht16k33_keypad_scan(struct ht16k33_keypad *keypad)
 {
 {
 	const unsigned short *keycodes = keypad->dev->keycode;
 	const unsigned short *keycodes = keypad->dev->keycode;
 	u16 new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
 	u16 new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
-	u8 data[HT16K33_MATRIX_KEYPAD_MAX_COLS * 2];
+	__le16 data[HT16K33_MATRIX_KEYPAD_MAX_COLS];
 	unsigned long bits_changed;
 	unsigned long bits_changed;
 	int row, col, code;
 	int row, col, code;
+	int rc;
 	bool pressed = false;
 	bool pressed = false;
 
 
-	if (i2c_smbus_read_i2c_block_data(keypad->client, 0x40, 6, data) != 6) {
-		dev_err(&keypad->client->dev, "Failed to read key data\n");
+	rc = i2c_smbus_read_i2c_block_data(keypad->client, 0x40,
+					   sizeof(data), (u8 *)data);
+	if (rc != sizeof(data)) {
+		dev_err(&keypad->client->dev,
+			"Failed to read key data, rc=%d\n", rc);
 		return false;
 		return false;
 	}
 	}
 
 
 	for (col = 0; col < keypad->cols; col++) {
 	for (col = 0; col < keypad->cols; col++) {
-		new_state[col] = (data[col * 2 + 1] << 8) | data[col * 2];
+		new_state[col] = le16_to_cpu(data[col]);
 		if (new_state[col])
 		if (new_state[col])
 			pressed = true;
 			pressed = true;
 		bits_changed = keypad->last_key_state[col] ^ new_state[col];
 		bits_changed = keypad->last_key_state[col] ^ new_state[col];
@@ -278,7 +282,7 @@ static bool ht16k33_keypad_scan(struct ht16k33_keypad *keypad)
 		}
 		}
 	}
 	}
 	input_sync(keypad->dev);
 	input_sync(keypad->dev);
-	memcpy(keypad->last_key_state, new_state, sizeof(new_state));
+	memcpy(keypad->last_key_state, new_state, sizeof(u16) * keypad->cols);
 
 
 	return pressed;
 	return pressed;
 }
 }
@@ -353,6 +357,12 @@ static int ht16k33_keypad_probe(struct i2c_client *client,
 	err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
 	err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
 	if (err)
 	if (err)
 		return err;
 		return err;
+	if (rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS ||
+	    cols > HT16K33_MATRIX_KEYPAD_MAX_COLS) {
+		dev_err(&client->dev, "%u rows or %u cols out of range in DT\n",
+			rows, cols);
+		return -ERANGE;
+	}
 
 
 	keypad->rows = rows;
 	keypad->rows = rows;
 	keypad->cols = cols;
 	keypad->cols = cols;

+ 1 - 0
drivers/auxdisplay/img-ascii-lcd.c

@@ -220,6 +220,7 @@ static const struct of_device_id img_ascii_lcd_matches[] = {
 	{ .compatible = "mti,sead3-lcd", .data = &sead3_config },
 	{ .compatible = "mti,sead3-lcd", .data = &sead3_config },
 	{ /* sentinel */ }
 	{ /* sentinel */ }
 };
 };
+MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
 
 
 /**
 /**
  * img_ascii_lcd_scroll() - scroll the display by a character
  * img_ascii_lcd_scroll() - scroll the display by a character

+ 92 - 735
drivers/misc/panel.c → drivers/auxdisplay/panel.c

@@ -1,6 +1,7 @@
 /*
 /*
  * Front panel driver for Linux
  * Front panel driver for Linux
  * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
  * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * modify it under the terms of the GNU General Public License
@@ -54,15 +55,12 @@
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/parport.h>
 #include <linux/parport.h>
 #include <linux/list.h>
 #include <linux/list.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <generated/utsrelease.h>
 
 
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
 
 
-#define LCD_MINOR		156
+#include <misc/charlcd.h>
+
 #define KEYPAD_MINOR		185
 #define KEYPAD_MINOR		185
 
 
 #define LCD_MAXBYTES		256	/* max burst write */
 #define LCD_MAXBYTES		256	/* max burst write */
@@ -76,9 +74,6 @@
 /* a key repeats this times INPUT_POLL_TIME */
 /* a key repeats this times INPUT_POLL_TIME */
 #define KEYPAD_REP_DELAY	(2)
 #define KEYPAD_REP_DELAY	(2)
 
 
-/* keep the light on this many seconds for each flash */
-#define FLASH_LIGHT_TEMPO	(4)
-
 /* converts an r_str() input to an active high, bits string : 000BAOSE */
 /* converts an r_str() input to an active high, bits string : 000BAOSE */
 #define PNL_PINPUT(a)		((((unsigned char)(a)) ^ 0x7F) >> 3)
 #define PNL_PINPUT(a)		((((unsigned char)(a)) ^ 0x7F) >> 3)
 
 
@@ -120,40 +115,6 @@
 #define PIN_SELECP		17
 #define PIN_SELECP		17
 #define PIN_NOT_SET		127
 #define PIN_NOT_SET		127
 
 
-#define LCD_FLAG_B		0x0004	/* blink on */
-#define LCD_FLAG_C		0x0008	/* cursor on */
-#define LCD_FLAG_D		0x0010	/* display on */
-#define LCD_FLAG_F		0x0020	/* large font mode */
-#define LCD_FLAG_N		0x0040	/* 2-rows mode */
-#define LCD_FLAG_L		0x0080	/* backlight enabled */
-
-/* LCD commands */
-#define LCD_CMD_DISPLAY_CLEAR	0x01	/* Clear entire display */
-
-#define LCD_CMD_ENTRY_MODE	0x04	/* Set entry mode */
-#define LCD_CMD_CURSOR_INC	0x02	/* Increment cursor */
-
-#define LCD_CMD_DISPLAY_CTRL	0x08	/* Display control */
-#define LCD_CMD_DISPLAY_ON	0x04	/* Set display on */
-#define LCD_CMD_CURSOR_ON	0x02	/* Set cursor on */
-#define LCD_CMD_BLINK_ON	0x01	/* Set blink on */
-
-#define LCD_CMD_SHIFT		0x10	/* Shift cursor/display */
-#define LCD_CMD_DISPLAY_SHIFT	0x08	/* Shift display instead of cursor */
-#define LCD_CMD_SHIFT_RIGHT	0x04	/* Shift display/cursor to the right */
-
-#define LCD_CMD_FUNCTION_SET	0x20	/* Set function */
-#define LCD_CMD_DATA_LEN_8BITS	0x10	/* Set data length to 8 bits */
-#define LCD_CMD_TWO_LINES	0x08	/* Set to two display lines */
-#define LCD_CMD_FONT_5X10_DOTS	0x04	/* Set char font to 5x10 dots */
-
-#define LCD_CMD_SET_CGRAM_ADDR	0x40	/* Set char generator RAM address */
-
-#define LCD_CMD_SET_DDRAM_ADDR	0x80	/* Set display data RAM address */
-
-#define LCD_ESCAPE_LEN		24	/* max chars for LCD escape command */
-#define LCD_ESCAPE_CHAR	27	/* use char 27 for escape command */
-
 #define NOT_SET			-1
 #define NOT_SET			-1
 
 
 /* macros to simplify use of the parallel port */
 /* macros to simplify use of the parallel port */
@@ -245,19 +206,10 @@ static wait_queue_head_t keypad_read_wait;
 static struct {
 static struct {
 	bool enabled;
 	bool enabled;
 	bool initialized;
 	bool initialized;
-	bool must_clear;
 
 
-	int height;
-	int width;
-	int bwidth;
-	int hwidth;
 	int charset;
 	int charset;
 	int proto;
 	int proto;
 
 
-	struct delayed_work bl_work;
-	struct mutex bl_tempo_lock;	/* Protects access to bl_tempo */
-	bool bl_tempo;
-
 	/* TODO: use union here? */
 	/* TODO: use union here? */
 	struct {
 	struct {
 		int e;
 		int e;
@@ -268,20 +220,7 @@ static struct {
 		int bl;
 		int bl;
 	} pins;
 	} pins;
 
 
-	/* contains the LCD config state */
-	unsigned long int flags;
-
-	/* Contains the LCD X and Y offset */
-	struct {
-		unsigned long int x;
-		unsigned long int y;
-	} addr;
-
-	/* Current escape sequence and it's length or -1 if outside */
-	struct {
-		char buf[LCD_ESCAPE_LEN + 1];
-		int len;
-	} esc_seq;
+	struct charlcd *charlcd;
 } lcd;
 } lcd;
 
 
 /* Needed only for init */
 /* Needed only for init */
@@ -464,17 +403,12 @@ static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];
 /* global variables */
 /* global variables */
 
 
 /* Device single-open policy control */
 /* Device single-open policy control */
-static atomic_t lcd_available = ATOMIC_INIT(1);
 static atomic_t keypad_available = ATOMIC_INIT(1);
 static atomic_t keypad_available = ATOMIC_INIT(1);
 
 
 static struct pardevice *pprt;
 static struct pardevice *pprt;
 
 
 static int keypad_initialized;
 static int keypad_initialized;
 
 
-static void (*lcd_write_cmd)(int);
-static void (*lcd_write_data)(int);
-static void (*lcd_clear_fast)(void);
-
 static DEFINE_SPINLOCK(pprt_lock);
 static DEFINE_SPINLOCK(pprt_lock);
 static struct timer_list scan_timer;
 static struct timer_list scan_timer;
 
 
@@ -574,8 +508,6 @@ static int keypad_enabled = NOT_SET;
 module_param(keypad_enabled, int, 0000);
 module_param(keypad_enabled, int, 0000);
 MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
 MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
 
 
-static const unsigned char *lcd_char_conv;
-
 /* for some LCD drivers (ks0074) we need a charset conversion table. */
 /* for some LCD drivers (ks0074) we need a charset conversion table. */
 static const unsigned char lcd_char_conv_ks0074[256] = {
 static const unsigned char lcd_char_conv_ks0074[256] = {
 	/*          0|8   1|9   2|A   3|B   4|C   5|D   6|E   7|F */
 	/*          0|8   1|9   2|A   3|B   4|C   5|D   6|E   7|F */
@@ -752,15 +684,6 @@ static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
 	}
 	}
 }
 }
 
 
-/* sleeps that many milliseconds with a reschedule */
-static void long_sleep(int ms)
-{
-	if (in_interrupt())
-		mdelay(ms);
-	else
-		schedule_timeout_interruptible(msecs_to_jiffies(ms));
-}
-
 /*
 /*
  * send a serial byte to the LCD panel. The caller is responsible for locking
  * send a serial byte to the LCD panel. The caller is responsible for locking
  * if needed.
  * if needed.
@@ -792,8 +715,11 @@ static void lcd_send_serial(int byte)
 }
 }
 
 
 /* turn the backlight on or off */
 /* turn the backlight on or off */
-static void __lcd_backlight(int on)
+static void lcd_backlight(struct charlcd *charlcd, int on)
 {
 {
+	if (lcd.pins.bl == PIN_NONE)
+		return;
+
 	/* The backlight is activated by setting the AUTOFEED line to +5V  */
 	/* The backlight is activated by setting the AUTOFEED line to +5V  */
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	if (on)
 	if (on)
@@ -804,46 +730,8 @@ static void __lcd_backlight(int on)
 	spin_unlock_irq(&pprt_lock);
 	spin_unlock_irq(&pprt_lock);
 }
 }
 
 
-static void lcd_backlight(int on)
-{
-	if (lcd.pins.bl == PIN_NONE)
-		return;
-
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (!lcd.bl_tempo)
-		__lcd_backlight(on);
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-static void lcd_bl_off(struct work_struct *work)
-{
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (lcd.bl_tempo) {
-		lcd.bl_tempo = false;
-		if (!(lcd.flags & LCD_FLAG_L))
-			__lcd_backlight(0);
-	}
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-/* turn the backlight on for a little while */
-static void lcd_poke(void)
-{
-	if (lcd.pins.bl == PIN_NONE)
-		return;
-
-	cancel_delayed_work_sync(&lcd.bl_work);
-
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L))
-		__lcd_backlight(1);
-	lcd.bl_tempo = true;
-	schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ);
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
 /* send a command to the LCD panel in serial mode */
 /* send a command to the LCD panel in serial mode */
-static void lcd_write_cmd_s(int cmd)
+static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	lcd_send_serial(0x1F);	/* R/W=W, RS=0 */
 	lcd_send_serial(0x1F);	/* R/W=W, RS=0 */
@@ -854,7 +742,7 @@ static void lcd_write_cmd_s(int cmd)
 }
 }
 
 
 /* send data to the LCD panel in serial mode */
 /* send data to the LCD panel in serial mode */
-static void lcd_write_data_s(int data)
+static void lcd_write_data_s(struct charlcd *charlcd, int data)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
 	lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
@@ -865,7 +753,7 @@ static void lcd_write_data_s(int data)
 }
 }
 
 
 /* send a command to the LCD panel in 8 bits parallel mode */
 /* send a command to the LCD panel in 8 bits parallel mode */
-static void lcd_write_cmd_p8(int cmd)
+static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
 	/* present the data to the data port */
@@ -887,7 +775,7 @@ static void lcd_write_cmd_p8(int cmd)
 }
 }
 
 
 /* send data to the LCD panel in 8 bits parallel mode */
 /* send data to the LCD panel in 8 bits parallel mode */
-static void lcd_write_data_p8(int data)
+static void lcd_write_data_p8(struct charlcd *charlcd, int data)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
 	/* present the data to the data port */
@@ -909,7 +797,7 @@ static void lcd_write_data_p8(int data)
 }
 }
 
 
 /* send a command to the TI LCD panel */
 /* send a command to the TI LCD panel */
-static void lcd_write_cmd_tilcd(int cmd)
+static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the control port */
 	/* present the data to the control port */
@@ -919,7 +807,7 @@ static void lcd_write_cmd_tilcd(int cmd)
 }
 }
 
 
 /* send data to the TI LCD panel */
 /* send data to the TI LCD panel */
-static void lcd_write_data_tilcd(int data)
+static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
 {
 {
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
 	/* present the data to the data port */
@@ -928,47 +816,13 @@ static void lcd_write_data_tilcd(int data)
 	spin_unlock_irq(&pprt_lock);
 	spin_unlock_irq(&pprt_lock);
 }
 }
 
 
-static void lcd_gotoxy(void)
-{
-	lcd_write_cmd(LCD_CMD_SET_DDRAM_ADDR
-		      | (lcd.addr.y ? lcd.hwidth : 0)
-		      /*
-		       * we force the cursor to stay at the end of the
-		       * line if it wants to go farther
-		       */
-		      | ((lcd.addr.x < lcd.bwidth) ? lcd.addr.x &
-			 (lcd.hwidth - 1) : lcd.bwidth - 1));
-}
-
-static void lcd_home(void)
-{
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
-}
-
-static void lcd_print(char c)
-{
-	if (lcd.addr.x < lcd.bwidth) {
-		if (lcd_char_conv)
-			c = lcd_char_conv[(unsigned char)c];
-		lcd_write_data(c);
-		lcd.addr.x++;
-	}
-	/* prevents the cursor from wrapping onto the next line */
-	if (lcd.addr.x == lcd.bwidth)
-		lcd_gotoxy();
-}
-
 /* fills the display with spaces and resets X/Y */
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_s(void)
+static void lcd_clear_fast_s(struct charlcd *charlcd)
 {
 {
 	int pos;
 	int pos;
 
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
 		lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
 		lcd_send_serial(' ' & 0x0F);
 		lcd_send_serial(' ' & 0x0F);
 		lcd_send_serial((' ' >> 4) & 0x0F);
 		lcd_send_serial((' ' >> 4) & 0x0F);
@@ -976,19 +830,15 @@ static void lcd_clear_fast_s(void)
 		udelay(40);
 		udelay(40);
 	}
 	}
 	spin_unlock_irq(&pprt_lock);
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
 }
 }
 
 
 /* fills the display with spaces and resets X/Y */
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_p8(void)
+static void lcd_clear_fast_p8(struct charlcd *charlcd)
 {
 {
 	int pos;
 	int pos;
 
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		/* present the data to the data port */
 		/* present the data to the data port */
 		w_dtr(pprt, ' ');
 		w_dtr(pprt, ' ');
 
 
@@ -1010,488 +860,62 @@ static void lcd_clear_fast_p8(void)
 		udelay(45);
 		udelay(45);
 	}
 	}
 	spin_unlock_irq(&pprt_lock);
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
 }
 }
 
 
 /* fills the display with spaces and resets X/Y */
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_tilcd(void)
+static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
 {
 {
 	int pos;
 	int pos;
 
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		/* present the data to the data port */
 		/* present the data to the data port */
 		w_dtr(pprt, ' ');
 		w_dtr(pprt, ' ');
 		udelay(60);
 		udelay(60);
 	}
 	}
 
 
 	spin_unlock_irq(&pprt_lock);
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
-}
-
-/* clears the display and resets X/Y */
-static void lcd_clear_display(void)
-{
-	lcd_write_cmd(LCD_CMD_DISPLAY_CLEAR);
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	/* we must wait a few milliseconds (15) */
-	long_sleep(15);
 }
 }
 
 
-static void lcd_init_display(void)
-{
-	lcd.flags = ((lcd.height > 1) ? LCD_FLAG_N : 0)
-	    | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
-
-	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
-
-	/* 8bits, 1 line, small fonts; let's do it 3 times */
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-
-	/* set font height and lines number */
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS
-		      | ((lcd.flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0)
-		      | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)
-	    );
-	long_sleep(10);
-
-	/* display off, cursor off, blink off */
-	lcd_write_cmd(LCD_CMD_DISPLAY_CTRL);
-	long_sleep(10);
-
-	lcd_write_cmd(LCD_CMD_DISPLAY_CTRL	/* set display mode */
-		      | ((lcd.flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0)
-		      | ((lcd.flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0)
-		      | ((lcd.flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)
-	    );
-
-	lcd_backlight((lcd.flags & LCD_FLAG_L) ? 1 : 0);
-
-	long_sleep(10);
-
-	/* entry mode set : increment, cursor shifting */
-	lcd_write_cmd(LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+static struct charlcd_ops charlcd_serial_ops = {
+	.write_cmd	= lcd_write_cmd_s,
+	.write_data	= lcd_write_data_s,
+	.clear_fast	= lcd_clear_fast_s,
+	.backlight	= lcd_backlight,
+};
 
 
-	lcd_clear_display();
-}
+static struct charlcd_ops charlcd_parallel_ops = {
+	.write_cmd	= lcd_write_cmd_p8,
+	.write_data	= lcd_write_data_p8,
+	.clear_fast	= lcd_clear_fast_p8,
+	.backlight	= lcd_backlight,
+};
 
 
-/*
- * These are the file operation function for user access to /dev/lcd
- * This function can also be called from inside the kernel, by
- * setting file and ppos to NULL.
- *
- */
+static struct charlcd_ops charlcd_tilcd_ops = {
+	.write_cmd	= lcd_write_cmd_tilcd,
+	.write_data	= lcd_write_data_tilcd,
+	.clear_fast	= lcd_clear_fast_tilcd,
+	.backlight	= lcd_backlight,
+};
 
 
-static inline int handle_lcd_special_code(void)
+/* initialize the LCD driver */
+static void lcd_init(void)
 {
 {
-	/* LCD special codes */
-
-	int processed = 0;
+	struct charlcd *charlcd;
 
 
-	char *esc = lcd.esc_seq.buf + 2;
-	int oldflags = lcd.flags;
-
-	/* check for display mode flags */
-	switch (*esc) {
-	case 'D':	/* Display ON */
-		lcd.flags |= LCD_FLAG_D;
-		processed = 1;
-		break;
-	case 'd':	/* Display OFF */
-		lcd.flags &= ~LCD_FLAG_D;
-		processed = 1;
-		break;
-	case 'C':	/* Cursor ON */
-		lcd.flags |= LCD_FLAG_C;
-		processed = 1;
-		break;
-	case 'c':	/* Cursor OFF */
-		lcd.flags &= ~LCD_FLAG_C;
-		processed = 1;
-		break;
-	case 'B':	/* Blink ON */
-		lcd.flags |= LCD_FLAG_B;
-		processed = 1;
-		break;
-	case 'b':	/* Blink OFF */
-		lcd.flags &= ~LCD_FLAG_B;
-		processed = 1;
-		break;
-	case '+':	/* Back light ON */
-		lcd.flags |= LCD_FLAG_L;
-		processed = 1;
-		break;
-	case '-':	/* Back light OFF */
-		lcd.flags &= ~LCD_FLAG_L;
-		processed = 1;
-		break;
-	case '*':
-		/* flash back light */
-		lcd_poke();
-		processed = 1;
-		break;
-	case 'f':	/* Small Font */
-		lcd.flags &= ~LCD_FLAG_F;
-		processed = 1;
-		break;
-	case 'F':	/* Large Font */
-		lcd.flags |= LCD_FLAG_F;
-		processed = 1;
-		break;
-	case 'n':	/* One Line */
-		lcd.flags &= ~LCD_FLAG_N;
-		processed = 1;
-		break;
-	case 'N':	/* Two Lines */
-		lcd.flags |= LCD_FLAG_N;
-		break;
-	case 'l':	/* Shift Cursor Left */
-		if (lcd.addr.x > 0) {
-			/* back one char if not at end of line */
-			if (lcd.addr.x < lcd.bwidth)
-				lcd_write_cmd(LCD_CMD_SHIFT);
-			lcd.addr.x--;
-		}
-		processed = 1;
-		break;
-	case 'r':	/* shift cursor right */
-		if (lcd.addr.x < lcd.width) {
-			/* allow the cursor to pass the end of the line */
-			if (lcd.addr.x < (lcd.bwidth - 1))
-				lcd_write_cmd(LCD_CMD_SHIFT |
-						LCD_CMD_SHIFT_RIGHT);
-			lcd.addr.x++;
-		}
-		processed = 1;
-		break;
-	case 'L':	/* shift display left */
-		lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
-		processed = 1;
-		break;
-	case 'R':	/* shift display right */
-		lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
-				LCD_CMD_SHIFT_RIGHT);
-		processed = 1;
-		break;
-	case 'k': {	/* kill end of line */
-		int x;
-
-		for (x = lcd.addr.x; x < lcd.bwidth; x++)
-			lcd_write_data(' ');
-
-		/* restore cursor position */
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-	case 'I':	/* reinitialize display */
-		lcd_init_display();
-		processed = 1;
-		break;
-	case 'G': {
-		/* Generator : LGcxxxxx...xx; must have <c> between '0'
-		 * and '7', representing the numerical ASCII code of the
-		 * redefined character, and <xx...xx> a sequence of 16
-		 * hex digits representing 8 bytes for each character.
-		 * Most LCDs will only use 5 lower bits of the 7 first
-		 * bytes.
-		 */
-
-		unsigned char cgbytes[8];
-		unsigned char cgaddr;
-		int cgoffset;
-		int shift;
-		char value;
-		int addr;
-
-		if (!strchr(esc, ';'))
-			break;
-
-		esc++;
-
-		cgaddr = *(esc++) - '0';
-		if (cgaddr > 7) {
-			processed = 1;
-			break;
-		}
-
-		cgoffset = 0;
-		shift = 0;
-		value = 0;
-		while (*esc && cgoffset < 8) {
-			shift ^= 4;
-			if (*esc >= '0' && *esc <= '9') {
-				value |= (*esc - '0') << shift;
-			} else if (*esc >= 'A' && *esc <= 'Z') {
-				value |= (*esc - 'A' + 10) << shift;
-			} else if (*esc >= 'a' && *esc <= 'z') {
-				value |= (*esc - 'a' + 10) << shift;
-			} else {
-				esc++;
-				continue;
-			}
-
-			if (shift == 0) {
-				cgbytes[cgoffset++] = value;
-				value = 0;
-			}
-
-			esc++;
-		}
-
-		lcd_write_cmd(LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
-		for (addr = 0; addr < cgoffset; addr++)
-			lcd_write_data(cgbytes[addr]);
-
-		/* ensures that we stop writing to CGRAM */
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-	case 'x':	/* gotoxy : LxXXX[yYYY]; */
-	case 'y':	/* gotoxy : LyYYY[xXXX]; */
-		if (!strchr(esc, ';'))
-			break;
-
-		while (*esc) {
-			if (*esc == 'x') {
-				esc++;
-				if (kstrtoul(esc, 10, &lcd.addr.x) < 0)
-					break;
-			} else if (*esc == 'y') {
-				esc++;
-				if (kstrtoul(esc, 10, &lcd.addr.y) < 0)
-					break;
-			} else {
-				break;
-			}
-		}
-
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-
-	/* TODO: This indent party here got ugly, clean it! */
-	/* Check whether one flag was changed */
-	if (oldflags != lcd.flags) {
-		/* check whether one of B,C,D flags were changed */
-		if ((oldflags ^ lcd.flags) &
-		    (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
-			/* set display mode */
-			lcd_write_cmd(LCD_CMD_DISPLAY_CTRL
-				      | ((lcd.flags & LCD_FLAG_D)
-						      ? LCD_CMD_DISPLAY_ON : 0)
-				      | ((lcd.flags & LCD_FLAG_C)
-						      ? LCD_CMD_CURSOR_ON : 0)
-				      | ((lcd.flags & LCD_FLAG_B)
-						      ? LCD_CMD_BLINK_ON : 0));
-		/* check whether one of F,N flags was changed */
-		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_F | LCD_FLAG_N))
-			lcd_write_cmd(LCD_CMD_FUNCTION_SET
-				      | LCD_CMD_DATA_LEN_8BITS
-				      | ((lcd.flags & LCD_FLAG_F)
-						      ? LCD_CMD_FONT_5X10_DOTS
-								      : 0)
-				      | ((lcd.flags & LCD_FLAG_N)
-						      ? LCD_CMD_TWO_LINES
-								      : 0));
-		/* check whether L flag was changed */
-		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L))
-			lcd_backlight(!!(lcd.flags & LCD_FLAG_L));
-	}
-
-	return processed;
-}
-
-static void lcd_write_char(char c)
-{
-	/* first, we'll test if we're in escape mode */
-	if ((c != '\n') && lcd.esc_seq.len >= 0) {
-		/* yes, let's add this char to the buffer */
-		lcd.esc_seq.buf[lcd.esc_seq.len++] = c;
-		lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
-	} else {
-		/* aborts any previous escape sequence */
-		lcd.esc_seq.len = -1;
-
-		switch (c) {
-		case LCD_ESCAPE_CHAR:
-			/* start of an escape sequence */
-			lcd.esc_seq.len = 0;
-			lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
-			break;
-		case '\b':
-			/* go back one char and clear it */
-			if (lcd.addr.x > 0) {
-				/*
-				 * check if we're not at the
-				 * end of the line
-				 */
-				if (lcd.addr.x < lcd.bwidth)
-					/* back one char */
-					lcd_write_cmd(LCD_CMD_SHIFT);
-				lcd.addr.x--;
-			}
-			/* replace with a space */
-			lcd_write_data(' ');
-			/* back one char again */
-			lcd_write_cmd(LCD_CMD_SHIFT);
-			break;
-		case '\014':
-			/* quickly clear the display */
-			lcd_clear_fast();
-			break;
-		case '\n':
-			/*
-			 * flush the remainder of the current line and
-			 * go to the beginning of the next line
-			 */
-			for (; lcd.addr.x < lcd.bwidth; lcd.addr.x++)
-				lcd_write_data(' ');
-			lcd.addr.x = 0;
-			lcd.addr.y = (lcd.addr.y + 1) % lcd.height;
-			lcd_gotoxy();
-			break;
-		case '\r':
-			/* go to the beginning of the same line */
-			lcd.addr.x = 0;
-			lcd_gotoxy();
-			break;
-		case '\t':
-			/* print a space instead of the tab */
-			lcd_print(' ');
-			break;
-		default:
-			/* simply print this char */
-			lcd_print(c);
-			break;
-		}
-	}
+	charlcd = charlcd_alloc(0);
+	if (!charlcd)
+		return;
 
 
 	/*
 	/*
-	 * now we'll see if we're in an escape mode and if the current
-	 * escape sequence can be understood.
+	 * Init lcd struct with load-time values to preserve exact
+	 * current functionality (at least for now).
 	 */
 	 */
-	if (lcd.esc_seq.len >= 2) {
-		int processed = 0;
-
-		if (!strcmp(lcd.esc_seq.buf, "[2J")) {
-			/* clear the display */
-			lcd_clear_fast();
-			processed = 1;
-		} else if (!strcmp(lcd.esc_seq.buf, "[H")) {
-			/* cursor to home */
-			lcd_home();
-			processed = 1;
-		}
-		/* codes starting with ^[[L */
-		else if ((lcd.esc_seq.len >= 3) &&
-			 (lcd.esc_seq.buf[0] == '[') &&
-			 (lcd.esc_seq.buf[1] == 'L')) {
-			processed = handle_lcd_special_code();
-		}
-
-		/* LCD special escape codes */
-		/*
-		 * flush the escape sequence if it's been processed
-		 * or if it is getting too long.
-		 */
-		if (processed || (lcd.esc_seq.len >= LCD_ESCAPE_LEN))
-			lcd.esc_seq.len = -1;
-	} /* escape codes */
-}
+	charlcd->height = lcd_height;
+	charlcd->width = lcd_width;
+	charlcd->bwidth = lcd_bwidth;
+	charlcd->hwidth = lcd_hwidth;
 
 
-static ssize_t lcd_write(struct file *file,
-			 const char __user *buf, size_t count, loff_t *ppos)
-{
-	const char __user *tmp = buf;
-	char c;
-
-	for (; count-- > 0; (*ppos)++, tmp++) {
-		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
-			/*
-			 * let's be a little nice with other processes
-			 * that need some CPU
-			 */
-			schedule();
-
-		if (get_user(c, tmp))
-			return -EFAULT;
-
-		lcd_write_char(c);
-	}
-
-	return tmp - buf;
-}
-
-static int lcd_open(struct inode *inode, struct file *file)
-{
-	if (!atomic_dec_and_test(&lcd_available))
-		return -EBUSY;	/* open only once at a time */
-
-	if (file->f_mode & FMODE_READ)	/* device is write-only */
-		return -EPERM;
-
-	if (lcd.must_clear) {
-		lcd_clear_display();
-		lcd.must_clear = false;
-	}
-	return nonseekable_open(inode, file);
-}
-
-static int lcd_release(struct inode *inode, struct file *file)
-{
-	atomic_inc(&lcd_available);
-	return 0;
-}
-
-static const struct file_operations lcd_fops = {
-	.write   = lcd_write,
-	.open    = lcd_open,
-	.release = lcd_release,
-	.llseek  = no_llseek,
-};
-
-static struct miscdevice lcd_dev = {
-	.minor	= LCD_MINOR,
-	.name	= "lcd",
-	.fops	= &lcd_fops,
-};
-
-/* public function usable from the kernel for any purpose */
-static void panel_lcd_print(const char *s)
-{
-	const char *tmp = s;
-	int count = strlen(s);
-
-	if (lcd.enabled && lcd.initialized) {
-		for (; count-- > 0; tmp++) {
-			if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
-				/*
-				 * let's be a little nice with other processes
-				 * that need some CPU
-				 */
-				schedule();
-
-			lcd_write_char(*tmp);
-		}
-	}
-}
-
-/* initialize the LCD driver */
-static void lcd_init(void)
-{
 	switch (selected_lcd_type) {
 	switch (selected_lcd_type) {
 	case LCD_TYPE_OLD:
 	case LCD_TYPE_OLD:
 		/* parallel mode, 8 bits */
 		/* parallel mode, 8 bits */
@@ -1500,10 +924,10 @@ static void lcd_init(void)
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.rs = PIN_AUTOLF;
 		lcd.pins.rs = PIN_AUTOLF;
 
 
-		lcd.width = 40;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 40;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 		break;
 	case LCD_TYPE_KS0074:
 	case LCD_TYPE_KS0074:
 		/* serial mode, ks0074 */
 		/* serial mode, ks0074 */
@@ -1513,10 +937,10 @@ static void lcd_init(void)
 		lcd.pins.cl = PIN_STROBE;
 		lcd.pins.cl = PIN_STROBE;
 		lcd.pins.da = PIN_D0;
 		lcd.pins.da = PIN_D0;
 
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 16;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 16;
+		charlcd->height = 2;
 		break;
 		break;
 	case LCD_TYPE_NEXCOM:
 	case LCD_TYPE_NEXCOM:
 		/* parallel mode, 8 bits, generic */
 		/* parallel mode, 8 bits, generic */
@@ -1526,10 +950,10 @@ static void lcd_init(void)
 		lcd.pins.rs = PIN_SELECP;
 		lcd.pins.rs = PIN_SELECP;
 		lcd.pins.rw = PIN_INITP;
 		lcd.pins.rw = PIN_INITP;
 
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 		break;
 	case LCD_TYPE_CUSTOM:
 	case LCD_TYPE_CUSTOM:
 		/* customer-defined */
 		/* customer-defined */
@@ -1545,22 +969,22 @@ static void lcd_init(void)
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.rs = PIN_SELECP;
 		lcd.pins.rs = PIN_SELECP;
 
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 		break;
 	}
 	}
 
 
 	/* Overwrite with module params set on loading */
 	/* Overwrite with module params set on loading */
 	if (lcd_height != NOT_SET)
 	if (lcd_height != NOT_SET)
-		lcd.height = lcd_height;
+		charlcd->height = lcd_height;
 	if (lcd_width != NOT_SET)
 	if (lcd_width != NOT_SET)
-		lcd.width = lcd_width;
+		charlcd->width = lcd_width;
 	if (lcd_bwidth != NOT_SET)
 	if (lcd_bwidth != NOT_SET)
-		lcd.bwidth = lcd_bwidth;
+		charlcd->bwidth = lcd_bwidth;
 	if (lcd_hwidth != NOT_SET)
 	if (lcd_hwidth != NOT_SET)
-		lcd.hwidth = lcd_hwidth;
+		charlcd->hwidth = lcd_hwidth;
 	if (lcd_charset != NOT_SET)
 	if (lcd_charset != NOT_SET)
 		lcd.charset = lcd_charset;
 		lcd.charset = lcd_charset;
 	if (lcd_proto != NOT_SET)
 	if (lcd_proto != NOT_SET)
@@ -1579,19 +1003,17 @@ static void lcd_init(void)
 		lcd.pins.bl = lcd_bl_pin;
 		lcd.pins.bl = lcd_bl_pin;
 
 
 	/* this is used to catch wrong and default values */
 	/* this is used to catch wrong and default values */
-	if (lcd.width <= 0)
-		lcd.width = DEFAULT_LCD_WIDTH;
-	if (lcd.bwidth <= 0)
-		lcd.bwidth = DEFAULT_LCD_BWIDTH;
-	if (lcd.hwidth <= 0)
-		lcd.hwidth = DEFAULT_LCD_HWIDTH;
-	if (lcd.height <= 0)
-		lcd.height = DEFAULT_LCD_HEIGHT;
+	if (charlcd->width <= 0)
+		charlcd->width = DEFAULT_LCD_WIDTH;
+	if (charlcd->bwidth <= 0)
+		charlcd->bwidth = DEFAULT_LCD_BWIDTH;
+	if (charlcd->hwidth <= 0)
+		charlcd->hwidth = DEFAULT_LCD_HWIDTH;
+	if (charlcd->height <= 0)
+		charlcd->height = DEFAULT_LCD_HEIGHT;
 
 
 	if (lcd.proto == LCD_PROTO_SERIAL) {	/* SERIAL */
 	if (lcd.proto == LCD_PROTO_SERIAL) {	/* SERIAL */
-		lcd_write_cmd = lcd_write_cmd_s;
-		lcd_write_data = lcd_write_data_s;
-		lcd_clear_fast = lcd_clear_fast_s;
+		charlcd->ops = &charlcd_serial_ops;
 
 
 		if (lcd.pins.cl == PIN_NOT_SET)
 		if (lcd.pins.cl == PIN_NOT_SET)
 			lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
 			lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1599,9 +1021,7 @@ static void lcd_init(void)
 			lcd.pins.da = DEFAULT_LCD_PIN_SDA;
 			lcd.pins.da = DEFAULT_LCD_PIN_SDA;
 
 
 	} else if (lcd.proto == LCD_PROTO_PARALLEL) {	/* PARALLEL */
 	} else if (lcd.proto == LCD_PROTO_PARALLEL) {	/* PARALLEL */
-		lcd_write_cmd = lcd_write_cmd_p8;
-		lcd_write_data = lcd_write_data_p8;
-		lcd_clear_fast = lcd_clear_fast_p8;
+		charlcd->ops = &charlcd_parallel_ops;
 
 
 		if (lcd.pins.e == PIN_NOT_SET)
 		if (lcd.pins.e == PIN_NOT_SET)
 			lcd.pins.e = DEFAULT_LCD_PIN_E;
 			lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1610,9 +1030,7 @@ static void lcd_init(void)
 		if (lcd.pins.rw == PIN_NOT_SET)
 		if (lcd.pins.rw == PIN_NOT_SET)
 			lcd.pins.rw = DEFAULT_LCD_PIN_RW;
 			lcd.pins.rw = DEFAULT_LCD_PIN_RW;
 	} else {
 	} else {
-		lcd_write_cmd = lcd_write_cmd_tilcd;
-		lcd_write_data = lcd_write_data_tilcd;
-		lcd_clear_fast = lcd_clear_fast_tilcd;
+		charlcd->ops = &charlcd_tilcd_ops;
 	}
 	}
 
 
 	if (lcd.pins.bl == PIN_NOT_SET)
 	if (lcd.pins.bl == PIN_NOT_SET)
@@ -1635,14 +1053,9 @@ static void lcd_init(void)
 		lcd.charset = DEFAULT_LCD_CHARSET;
 		lcd.charset = DEFAULT_LCD_CHARSET;
 
 
 	if (lcd.charset == LCD_CHARSET_KS0074)
 	if (lcd.charset == LCD_CHARSET_KS0074)
-		lcd_char_conv = lcd_char_conv_ks0074;
+		charlcd->char_conv = lcd_char_conv_ks0074;
 	else
 	else
-		lcd_char_conv = NULL;
-
-	if (lcd.pins.bl != PIN_NONE) {
-		mutex_init(&lcd.bl_tempo_lock);
-		INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off);
-	}
+		charlcd->char_conv = NULL;
 
 
 	pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
 	pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
 		    lcd_bits[LCD_PORT_C][LCD_BIT_E]);
 		    lcd_bits[LCD_PORT_C][LCD_BIT_E]);
@@ -1657,25 +1070,8 @@ static void lcd_init(void)
 	pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
 	pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
 		    lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
 		    lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
 
 
-	/*
-	 * before this line, we must NOT send anything to the display.
-	 * Since lcd_init_display() needs to write data, we have to
-	 * enable mark the LCD initialized just before.
-	 */
+	lcd.charlcd = charlcd;
 	lcd.initialized = true;
 	lcd.initialized = true;
-	lcd_init_display();
-
-	/* display a short message */
-#ifdef CONFIG_PANEL_CHANGE_MESSAGE
-#ifdef CONFIG_PANEL_BOOT_MESSAGE
-	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
-#endif
-#else
-	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
-#endif
-	/* clear the display on the next device opening */
-	lcd.must_clear = true;
-	lcd_home();
 }
 }
 
 
 /*
 /*
@@ -2011,7 +1407,7 @@ static void panel_scan_timer(void)
 	}
 	}
 
 
 	if (keypressed && lcd.enabled && lcd.initialized)
 	if (keypressed && lcd.enabled && lcd.initialized)
-		lcd_poke();
+		charlcd_poke(lcd.charlcd);
 
 
 	mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
 	mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
 }
 }
@@ -2175,35 +1571,6 @@ static void keypad_init(void)
 /* device initialization                          */
 /* device initialization                          */
 /**************************************************/
 /**************************************************/
 
 
-static int panel_notify_sys(struct notifier_block *this, unsigned long code,
-			    void *unused)
-{
-	if (lcd.enabled && lcd.initialized) {
-		switch (code) {
-		case SYS_DOWN:
-			panel_lcd_print
-			    ("\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		case SYS_HALT:
-			panel_lcd_print
-			    ("\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		case SYS_POWER_OFF:
-			panel_lcd_print("\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		default:
-			break;
-		}
-	}
-	return NOTIFY_DONE;
-}
-
-static struct notifier_block panel_notifier = {
-	panel_notify_sys,
-	NULL,
-	0
-};
-
 static void panel_attach(struct parport *port)
 static void panel_attach(struct parport *port)
 {
 {
 	struct pardev_cb panel_cb;
 	struct pardev_cb panel_cb;
@@ -2239,7 +1606,7 @@ static void panel_attach(struct parport *port)
 	 */
 	 */
 	if (lcd.enabled) {
 	if (lcd.enabled) {
 		lcd_init();
 		lcd_init();
-		if (misc_register(&lcd_dev))
+		if (!lcd.charlcd || charlcd_register(lcd.charlcd))
 			goto err_unreg_device;
 			goto err_unreg_device;
 	}
 	}
 
 
@@ -2248,13 +1615,14 @@ static void panel_attach(struct parport *port)
 		if (misc_register(&keypad_dev))
 		if (misc_register(&keypad_dev))
 			goto err_lcd_unreg;
 			goto err_lcd_unreg;
 	}
 	}
-	register_reboot_notifier(&panel_notifier);
 	return;
 	return;
 
 
 err_lcd_unreg:
 err_lcd_unreg:
 	if (lcd.enabled)
 	if (lcd.enabled)
-		misc_deregister(&lcd_dev);
+		charlcd_unregister(lcd.charlcd);
 err_unreg_device:
 err_unreg_device:
+	kfree(lcd.charlcd);
+	lcd.charlcd = NULL;
 	parport_unregister_device(pprt);
 	parport_unregister_device(pprt);
 	pprt = NULL;
 	pprt = NULL;
 }
 }
@@ -2278,20 +1646,16 @@ static void panel_detach(struct parport *port)
 	}
 	}
 
 
 	if (lcd.enabled) {
 	if (lcd.enabled) {
-		panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
-		misc_deregister(&lcd_dev);
-		if (lcd.pins.bl != PIN_NONE) {
-			cancel_delayed_work_sync(&lcd.bl_work);
-			__lcd_backlight(0);
-		}
+		charlcd_unregister(lcd.charlcd);
 		lcd.initialized = false;
 		lcd.initialized = false;
+		kfree(lcd.charlcd);
+		lcd.charlcd = NULL;
 	}
 	}
 
 
 	/* TODO: free all input signals */
 	/* TODO: free all input signals */
 	parport_release(pprt);
 	parport_release(pprt);
 	parport_unregister_device(pprt);
 	parport_unregister_device(pprt);
 	pprt = NULL;
 	pprt = NULL;
-	unregister_reboot_notifier(&panel_notifier);
 }
 }
 
 
 static struct parport_driver panel_driver = {
 static struct parport_driver panel_driver = {
@@ -2369,10 +1733,6 @@ static int __init panel_init_module(void)
 		 * Init lcd struct with load-time values to preserve exact
 		 * Init lcd struct with load-time values to preserve exact
 		 * current functionality (at least for now).
 		 * current functionality (at least for now).
 		 */
 		 */
-		lcd.height = lcd_height;
-		lcd.width = lcd_width;
-		lcd.bwidth = lcd_bwidth;
-		lcd.hwidth = lcd_hwidth;
 		lcd.charset = lcd_charset;
 		lcd.charset = lcd_charset;
 		lcd.proto = lcd_proto;
 		lcd.proto = lcd_proto;
 		lcd.pins.e = lcd_e_pin;
 		lcd.pins.e = lcd_e_pin;
@@ -2381,9 +1741,6 @@ static int __init panel_init_module(void)
 		lcd.pins.cl = lcd_cl_pin;
 		lcd.pins.cl = lcd_cl_pin;
 		lcd.pins.da = lcd_da_pin;
 		lcd.pins.da = lcd_da_pin;
 		lcd.pins.bl = lcd_bl_pin;
 		lcd.pins.bl = lcd_bl_pin;
-
-		/* Leave it for now, just in case */
-		lcd.esc_seq.len = -1;
 	}
 	}
 
 
 	switch (selected_keypad_type) {
 	switch (selected_keypad_type) {

+ 1 - 1
drivers/char/hangcheck-timer.c

@@ -32,7 +32,7 @@
  * timer and 180 seconds for the margin of error.  IOW, a timer is set
  * timer and 180 seconds for the margin of error.  IOW, a timer is set
  * for 60 seconds.  When the timer fires, the callback checks the
  * for 60 seconds.  When the timer fires, the callback checks the
  * actual duration that the timer waited.  If the duration exceeds the
  * actual duration that the timer waited.  If the duration exceeds the
- * alloted time and margin (here 60 + 180, or 240 seconds), the machine
+ * allotted time and margin (here 60 + 180, or 240 seconds), the machine
  * is restarted.  A healthy machine will have the duration match the
  * is restarted.  A healthy machine will have the duration match the
  * expected timeout very closely.
  * expected timeout very closely.
  */
  */

+ 1 - 1
drivers/char/hpet.c

@@ -575,7 +575,7 @@ static inline unsigned long hpet_time_div(struct hpets *hpets,
 }
 }
 
 
 static int
 static int
-hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+hpet_ioctl_common(struct hpet_dev *devp, unsigned int cmd, unsigned long arg,
 		  struct hpet_info *info)
 		  struct hpet_info *info)
 {
 {
 	struct hpet_timer __iomem *timer;
 	struct hpet_timer __iomem *timer;

+ 6 - 5
drivers/char/misc.c

@@ -109,7 +109,7 @@ static const struct file_operations misc_proc_fops = {
 };
 };
 #endif
 #endif
 
 
-static int misc_open(struct inode * inode, struct file * file)
+static int misc_open(struct inode *inode, struct file *file)
 {
 {
 	int minor = iminor(inode);
 	int minor = iminor(inode);
 	struct miscdevice *c;
 	struct miscdevice *c;
@@ -150,7 +150,7 @@ static int misc_open(struct inode * inode, struct file * file)
 	err = 0;
 	err = 0;
 	replace_fops(file, new_fops);
 	replace_fops(file, new_fops);
 	if (file->f_op->open)
 	if (file->f_op->open)
-		err = file->f_op->open(inode,file);
+		err = file->f_op->open(inode, file);
 fail:
 fail:
 	mutex_unlock(&misc_mtx);
 	mutex_unlock(&misc_mtx);
 	return err;
 	return err;
@@ -182,7 +182,7 @@ static const struct file_operations misc_fops = {
  *	failure.
  *	failure.
  */
  */
 
 
-int misc_register(struct miscdevice * misc)
+int misc_register(struct miscdevice *misc)
 {
 {
 	dev_t dev;
 	dev_t dev;
 	int err = 0;
 	int err = 0;
@@ -194,6 +194,7 @@ int misc_register(struct miscdevice * misc)
 
 
 	if (is_dynamic) {
 	if (is_dynamic) {
 		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
 		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
+
 		if (i >= DYNAMIC_MINORS) {
 		if (i >= DYNAMIC_MINORS) {
 			err = -EBUSY;
 			err = -EBUSY;
 			goto out;
 			goto out;
@@ -287,13 +288,13 @@ static int __init misc_init(void)
 		goto fail_remove;
 		goto fail_remove;
 
 
 	err = -EIO;
 	err = -EIO;
-	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
+	if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
 		goto fail_printk;
 		goto fail_printk;
 	misc_class->devnode = misc_devnode;
 	misc_class->devnode = misc_devnode;
 	return 0;
 	return 0;
 
 
 fail_printk:
 fail_printk:
-	printk("unable to get major %d for misc devices\n", MISC_MAJOR);
+	pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
 	class_destroy(misc_class);
 	class_destroy(misc_class);
 fail_remove:
 fail_remove:
 	if (ret)
 	if (ret)

+ 5 - 4
drivers/char/mspec.c

@@ -43,6 +43,7 @@
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/numa.h>
 #include <linux/numa.h>
+#include <linux/refcount.h>
 #include <asm/page.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <linux/atomic.h>
 #include <linux/atomic.h>
@@ -89,7 +90,7 @@ static int is_sn2;
  * protect in fork case where multiple tasks share the vma_data.
  * protect in fork case where multiple tasks share the vma_data.
  */
  */
 struct vma_data {
 struct vma_data {
-	atomic_t refcnt;	/* Number of vmas sharing the data. */
+	refcount_t refcnt;	/* Number of vmas sharing the data. */
 	spinlock_t lock;	/* Serialize access to this structure. */
 	spinlock_t lock;	/* Serialize access to this structure. */
 	int count;		/* Number of pages allocated. */
 	int count;		/* Number of pages allocated. */
 	enum mspec_page_type type; /* Type of pages allocated. */
 	enum mspec_page_type type; /* Type of pages allocated. */
@@ -144,7 +145,7 @@ mspec_open(struct vm_area_struct *vma)
 	struct vma_data *vdata;
 	struct vma_data *vdata;
 
 
 	vdata = vma->vm_private_data;
 	vdata = vma->vm_private_data;
-	atomic_inc(&vdata->refcnt);
+	refcount_inc(&vdata->refcnt);
 }
 }
 
 
 /*
 /*
@@ -162,7 +163,7 @@ mspec_close(struct vm_area_struct *vma)
 
 
 	vdata = vma->vm_private_data;
 	vdata = vma->vm_private_data;
 
 
-	if (!atomic_dec_and_test(&vdata->refcnt))
+	if (!refcount_dec_and_test(&vdata->refcnt))
 		return;
 		return;
 
 
 	last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT;
 	last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT;
@@ -274,7 +275,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
 	vdata->vm_end = vma->vm_end;
 	vdata->vm_end = vma->vm_end;
 	vdata->type = type;
 	vdata->type = type;
 	spin_lock_init(&vdata->lock);
 	spin_lock_init(&vdata->lock);
-	atomic_set(&vdata->refcnt, 1);
+	refcount_set(&vdata->refcnt, 1);
 	vma->vm_private_data = vdata;
 	vma->vm_private_data = vdata;
 
 
 	vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
 	vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;

+ 14 - 40
drivers/char/tpm/tpm-chip.c

@@ -218,8 +218,6 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
 	cdev_init(&chip->cdevs, &tpmrm_fops);
 	cdev_init(&chip->cdevs, &tpmrm_fops);
 	chip->cdev.owner = THIS_MODULE;
 	chip->cdev.owner = THIS_MODULE;
 	chip->cdevs.owner = THIS_MODULE;
 	chip->cdevs.owner = THIS_MODULE;
-	chip->cdev.kobj.parent = &chip->dev.kobj;
-	chip->cdevs.kobj.parent = &chip->devs.kobj;
 
 
 	chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
 	chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
 	if (!chip->work_space.context_buf) {
 	if (!chip->work_space.context_buf) {
@@ -275,45 +273,24 @@ static int tpm_add_char_device(struct tpm_chip *chip)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = cdev_add(&chip->cdev, chip->dev.devt, 1);
+	rc = cdev_device_add(&chip->cdev, &chip->dev);
 	if (rc) {
 	if (rc) {
 		dev_err(&chip->dev,
 		dev_err(&chip->dev,
-			"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
+			"unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",
 			dev_name(&chip->dev), MAJOR(chip->dev.devt),
 			dev_name(&chip->dev), MAJOR(chip->dev.devt),
 			MINOR(chip->dev.devt), rc);
 			MINOR(chip->dev.devt), rc);
 		return rc;
 		return rc;
 	}
 	}
 
 
-	rc = device_add(&chip->dev);
-	if (rc) {
-		dev_err(&chip->dev,
-			"unable to device_register() %s, major %d, minor %d, err=%d\n",
-			dev_name(&chip->dev), MAJOR(chip->dev.devt),
-			MINOR(chip->dev.devt), rc);
-
-		cdev_del(&chip->cdev);
-		return rc;
-	}
-
-	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		rc = cdev_add(&chip->cdevs, chip->devs.devt, 1);
-	if (rc) {
-		dev_err(&chip->dev,
-			"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
-			dev_name(&chip->devs), MAJOR(chip->devs.devt),
-			MINOR(chip->devs.devt), rc);
-		return rc;
-	}
-
-	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		rc = device_add(&chip->devs);
-	if (rc) {
-		dev_err(&chip->dev,
-			"unable to device_register() %s, major %d, minor %d, err=%d\n",
-			dev_name(&chip->devs), MAJOR(chip->devs.devt),
-			MINOR(chip->devs.devt), rc);
-		cdev_del(&chip->cdevs);
-		return rc;
+	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+		rc = cdev_device_add(&chip->cdevs, &chip->devs);
+		if (rc) {
+			dev_err(&chip->devs,
+				"unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",
+				dev_name(&chip->devs), MAJOR(chip->devs.devt),
+				MINOR(chip->devs.devt), rc);
+			return rc;
+		}
 	}
 	}
 
 
 	/* Make the chip available. */
 	/* Make the chip available. */
@@ -326,8 +303,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
 
 
 static void tpm_del_char_device(struct tpm_chip *chip)
 static void tpm_del_char_device(struct tpm_chip *chip)
 {
 {
-	cdev_del(&chip->cdev);
-	device_del(&chip->dev);
+	cdev_device_del(&chip->cdev, &chip->dev);
 
 
 	/* Make the chip unavailable. */
 	/* Make the chip unavailable. */
 	mutex_lock(&idr_lock);
 	mutex_lock(&idr_lock);
@@ -449,10 +425,8 @@ void tpm_chip_unregister(struct tpm_chip *chip)
 {
 {
 	tpm_del_legacy_sysfs(chip);
 	tpm_del_legacy_sysfs(chip);
 	tpm_bios_log_teardown(chip);
 	tpm_bios_log_teardown(chip);
-	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-		cdev_del(&chip->cdevs);
-		device_del(&chip->devs);
-	}
+	if (chip->flags & TPM_CHIP_FLAG_TPM2)
+		cdev_device_del(&chip->cdevs, &chip->devs);
 	tpm_del_char_device(chip);
 	tpm_del_char_device(chip);
 }
 }
 EXPORT_SYMBOL_GPL(tpm_chip_unregister);
 EXPORT_SYMBOL_GPL(tpm_chip_unregister);

+ 1 - 1
drivers/char/virtio_console.c

@@ -2304,7 +2304,7 @@ static int __init init(void)
 
 
 	pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
 	pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
 	if (!pdrvdata.debugfs_dir)
 	if (!pdrvdata.debugfs_dir)
-		pr_warning("Error creating debugfs dir for virtio-ports\n");
+		pr_warn("Error creating debugfs dir for virtio-ports\n");
 	INIT_LIST_HEAD(&pdrvdata.consoles);
 	INIT_LIST_HEAD(&pdrvdata.consoles);
 	INIT_LIST_HEAD(&pdrvdata.portdevs);
 	INIT_LIST_HEAD(&pdrvdata.portdevs);
 
 

+ 16 - 17
drivers/dax/dax.c

@@ -703,13 +703,8 @@ static void dax_dev_release(struct device *dev)
 	kfree(dax_dev);
 	kfree(dax_dev);
 }
 }
 
 
-static void unregister_dax_dev(void *dev)
+static void kill_dax_dev(struct dax_dev *dax_dev)
 {
 {
-	struct dax_dev *dax_dev = to_dax_dev(dev);
-	struct cdev *cdev = &dax_dev->cdev;
-
-	dev_dbg(dev, "%s\n", __func__);
-
 	/*
 	/*
 	 * Note, rcu is not protecting the liveness of dax_dev, rcu is
 	 * Note, rcu is not protecting the liveness of dax_dev, rcu is
 	 * ensuring that any fault handlers that might have seen
 	 * ensuring that any fault handlers that might have seen
@@ -720,8 +715,17 @@ static void unregister_dax_dev(void *dev)
 	dax_dev->alive = false;
 	dax_dev->alive = false;
 	synchronize_srcu(&dax_srcu);
 	synchronize_srcu(&dax_srcu);
 	unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);
 	unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);
-	cdev_del(cdev);
-	device_unregister(dev);
+}
+
+static void unregister_dax_dev(void *dev)
+{
+	struct dax_dev *dax_dev = to_dax_dev(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	kill_dax_dev(dax_dev);
+	cdev_device_del(&dax_dev->cdev, dev);
+	put_device(dev);
 }
 }
 
 
 struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
 struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
@@ -772,18 +776,13 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
 		goto err_inode;
 		goto err_inode;
 	}
 	}
 
 
-	/* device_initialize() so cdev can reference kobj parent */
+	/* from here on we're committed to teardown via dax_dev_release() */
 	device_initialize(dev);
 	device_initialize(dev);
 
 
 	cdev = &dax_dev->cdev;
 	cdev = &dax_dev->cdev;
 	cdev_init(cdev, &dax_fops);
 	cdev_init(cdev, &dax_fops);
 	cdev->owner = parent->driver->owner;
 	cdev->owner = parent->driver->owner;
-	cdev->kobj.parent = &dev->kobj;
-	rc = cdev_add(&dax_dev->cdev, dev_t, 1);
-	if (rc)
-		goto err_cdev;
 
 
-	/* from here on we're committed to teardown via dax_dev_release() */
 	dax_dev->num_resources = count;
 	dax_dev->num_resources = count;
 	dax_dev->alive = true;
 	dax_dev->alive = true;
 	dax_dev->region = dax_region;
 	dax_dev->region = dax_region;
@@ -795,8 +794,10 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
 	dev->groups = dax_attribute_groups;
 	dev->groups = dax_attribute_groups;
 	dev->release = dax_dev_release;
 	dev->release = dax_dev_release;
 	dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
 	dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
-	rc = device_add(dev);
+
+	rc = cdev_device_add(cdev, dev);
 	if (rc) {
 	if (rc) {
+		kill_dax_dev(dax_dev);
 		put_device(dev);
 		put_device(dev);
 		return ERR_PTR(rc);
 		return ERR_PTR(rc);
 	}
 	}
@@ -807,8 +808,6 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
 
 
 	return dax_dev;
 	return dax_dev;
 
 
- err_cdev:
-	iput(dax_dev->inode);
  err_inode:
  err_inode:
 	ida_simple_remove(&dax_minor_ida, minor);
 	ida_simple_remove(&dax_minor_ida, minor);
  err_minor:
  err_minor:

+ 7 - 0
drivers/extcon/Kconfig

@@ -52,6 +52,13 @@ config EXTCON_INTEL_INT3496
 	  This ACPI device is typically found on Intel Baytrail or Cherrytrail
 	  This ACPI device is typically found on Intel Baytrail or Cherrytrail
 	  based tablets, or other Baytrail / Cherrytrail devices.
 	  based tablets, or other Baytrail / Cherrytrail devices.
 
 
+config EXTCON_INTEL_CHT_WC
+	tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver"
+	depends on INTEL_SOC_PMIC_CHTWC
+	help
+	  Say Y here to enable extcon support for charger detection / control
+	  on the Intel Cherrytrail Whiskey Cove PMIC.
+
 config EXTCON_MAX14577
 config EXTCON_MAX14577
 	tristate "Maxim MAX14577/77836 EXTCON Support"
 	tristate "Maxim MAX14577/77836 EXTCON Support"
 	depends on MFD_MAX14577
 	depends on MFD_MAX14577

+ 1 - 0
drivers/extcon/Makefile

@@ -9,6 +9,7 @@ obj-$(CONFIG_EXTCON_ARIZONA)	+= extcon-arizona.o
 obj-$(CONFIG_EXTCON_AXP288)	+= extcon-axp288.o
 obj-$(CONFIG_EXTCON_AXP288)	+= extcon-axp288.o
 obj-$(CONFIG_EXTCON_GPIO)	+= extcon-gpio.o
 obj-$(CONFIG_EXTCON_GPIO)	+= extcon-gpio.o
 obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o
 obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o
+obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o
 obj-$(CONFIG_EXTCON_MAX14577)	+= extcon-max14577.o
 obj-$(CONFIG_EXTCON_MAX14577)	+= extcon-max14577.o
 obj-$(CONFIG_EXTCON_MAX3355)	+= extcon-max3355.o
 obj-$(CONFIG_EXTCON_MAX3355)	+= extcon-max3355.o
 obj-$(CONFIG_EXTCON_MAX77693)	+= extcon-max77693.o
 obj-$(CONFIG_EXTCON_MAX77693)	+= extcon-max77693.o

+ 46 - 0
drivers/extcon/extcon-arizona.c

@@ -51,6 +51,9 @@
 #define HPDET_DEBOUNCE 500
 #define HPDET_DEBOUNCE 500
 #define DEFAULT_MICD_TIMEOUT 2000
 #define DEFAULT_MICD_TIMEOUT 2000
 
 
+#define ARIZONA_HPDET_WAIT_COUNT 15
+#define ARIZONA_HPDET_WAIT_DELAY_MS 20
+
 #define QUICK_HEADPHONE_MAX_OHM 3
 #define QUICK_HEADPHONE_MAX_OHM 3
 #define MICROPHONE_MIN_OHM      1257
 #define MICROPHONE_MIN_OHM      1257
 #define MICROPHONE_MAX_OHM      30000
 #define MICROPHONE_MAX_OHM      30000
@@ -1049,6 +1052,40 @@ static void arizona_hpdet_work(struct work_struct *work)
 	mutex_unlock(&info->lock);
 	mutex_unlock(&info->lock);
 }
 }
 
 
+static int arizona_hpdet_wait(struct arizona_extcon_info *info)
+{
+	struct arizona *arizona = info->arizona;
+	unsigned int val;
+	int i, ret;
+
+	for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) {
+		ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
+				&val);
+		if (ret) {
+			dev_err(arizona->dev,
+				"Failed to read HPDET state: %d\n", ret);
+			return ret;
+		}
+
+		switch (info->hpdet_ip_version) {
+		case 0:
+			if (val & ARIZONA_HP_DONE)
+				return 0;
+			break;
+		default:
+			if (val & ARIZONA_HP_DONE_B)
+				return 0;
+			break;
+		}
+
+		msleep(ARIZONA_HPDET_WAIT_DELAY_MS);
+	}
+
+	dev_warn(arizona->dev, "HPDET did not appear to complete\n");
+
+	return -ETIMEDOUT;
+}
+
 static irqreturn_t arizona_jackdet(int irq, void *data)
 static irqreturn_t arizona_jackdet(int irq, void *data)
 {
 {
 	struct arizona_extcon_info *info = data;
 	struct arizona_extcon_info *info = data;
@@ -1155,6 +1192,15 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
 					"Removal report failed: %d\n", ret);
 					"Removal report failed: %d\n", ret);
 		}
 		}
 
 
+		/*
+		 * If the jack was removed during a headphone detection we
+		 * need to wait for the headphone detection to finish, as
+		 * it can not be aborted. We don't want to be able to start
+		 * a new headphone detection from a fresh insert until this
+		 * one is finished.
+		 */
+		arizona_hpdet_wait(info);
+
 		regmap_update_bits(arizona->regmap,
 		regmap_update_bits(arizona->regmap,
 				   ARIZONA_JACK_DETECT_DEBOUNCE,
 				   ARIZONA_JACK_DETECT_DEBOUNCE,
 				   ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
 				   ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,

+ 395 - 0
drivers/extcon/extcon-intel-cht-wc.c

@@ -0,0 +1,395 @@
+/*
+ * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define CHT_WC_PHYCTRL			0x5e07
+
+#define CHT_WC_CHGRCTRL0		0x5e16
+#define CHT_WC_CHGRCTRL0_CHGRRESET	BIT(0)
+#define CHT_WC_CHGRCTRL0_EMRGCHREN	BIT(1)
+#define CHT_WC_CHGRCTRL0_EXTCHRDIS	BIT(2)
+#define CHT_WC_CHGRCTRL0_SWCONTROL	BIT(3)
+#define CHT_WC_CHGRCTRL0_TTLCK_MASK	BIT(4)
+#define CHT_WC_CHGRCTRL0_CCSM_OFF_MASK	BIT(5)
+#define CHT_WC_CHGRCTRL0_DBPOFF_MASK	BIT(6)
+#define CHT_WC_CHGRCTRL0_WDT_NOKICK	BIT(7)
+
+#define CHT_WC_CHGRCTRL1		0x5e17
+
+#define CHT_WC_USBSRC			0x5e29
+#define CHT_WC_USBSRC_STS_MASK		GENMASK(1, 0)
+#define CHT_WC_USBSRC_STS_SUCCESS	2
+#define CHT_WC_USBSRC_STS_FAIL		3
+#define CHT_WC_USBSRC_TYPE_SHIFT	2
+#define CHT_WC_USBSRC_TYPE_MASK		GENMASK(5, 2)
+#define CHT_WC_USBSRC_TYPE_NONE		0
+#define CHT_WC_USBSRC_TYPE_SDP		1
+#define CHT_WC_USBSRC_TYPE_DCP		2
+#define CHT_WC_USBSRC_TYPE_CDP		3
+#define CHT_WC_USBSRC_TYPE_ACA		4
+#define CHT_WC_USBSRC_TYPE_SE1		5
+#define CHT_WC_USBSRC_TYPE_MHL		6
+#define CHT_WC_USBSRC_TYPE_FLOAT_DP_DN	7
+#define CHT_WC_USBSRC_TYPE_OTHER	8
+#define CHT_WC_USBSRC_TYPE_DCP_EXTPHY	9
+
+#define CHT_WC_PWRSRC_IRQ		0x6e03
+#define CHT_WC_PWRSRC_IRQ_MASK		0x6e0f
+#define CHT_WC_PWRSRC_STS		0x6e1e
+#define CHT_WC_PWRSRC_VBUS		BIT(0)
+#define CHT_WC_PWRSRC_DC		BIT(1)
+#define CHT_WC_PWRSRC_BAT		BIT(2)
+#define CHT_WC_PWRSRC_ID_GND		BIT(3)
+#define CHT_WC_PWRSRC_ID_FLOAT		BIT(4)
+
+#define CHT_WC_VBUS_GPIO_CTLO		0x6e2d
+#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT	BIT(0)
+
+enum cht_wc_usb_id {
+	USB_ID_OTG,
+	USB_ID_GND,
+	USB_ID_FLOAT,
+	USB_RID_A,
+	USB_RID_B,
+	USB_RID_C,
+};
+
+enum cht_wc_mux_select {
+	MUX_SEL_PMIC = 0,
+	MUX_SEL_SOC,
+};
+
+static const unsigned int cht_wc_extcon_cables[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_CHG_USB_SDP,
+	EXTCON_CHG_USB_CDP,
+	EXTCON_CHG_USB_DCP,
+	EXTCON_CHG_USB_ACA,
+	EXTCON_NONE,
+};
+
+struct cht_wc_extcon_data {
+	struct device *dev;
+	struct regmap *regmap;
+	struct extcon_dev *edev;
+	unsigned int previous_cable;
+	bool usb_host;
+};
+
+static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
+{
+	if (pwrsrc_sts & CHT_WC_PWRSRC_ID_GND)
+		return USB_ID_GND;
+	if (pwrsrc_sts & CHT_WC_PWRSRC_ID_FLOAT)
+		return USB_ID_FLOAT;
+
+	/*
+	 * Once we have iio support for the gpadc we should read the USBID
+	 * gpadc channel here and determine ACA role based on that.
+	 */
+	return USB_ID_FLOAT;
+}
+
+static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
+				     bool ignore_errors)
+{
+	int ret, usbsrc, status;
+	unsigned long timeout;
+
+	/* Charger detection can take upto 600ms, wait 800ms max. */
+	timeout = jiffies + msecs_to_jiffies(800);
+	do {
+		ret = regmap_read(ext->regmap, CHT_WC_USBSRC, &usbsrc);
+		if (ret) {
+			dev_err(ext->dev, "Error reading usbsrc: %d\n", ret);
+			return ret;
+		}
+
+		status = usbsrc & CHT_WC_USBSRC_STS_MASK;
+		if (status == CHT_WC_USBSRC_STS_SUCCESS ||
+		    status == CHT_WC_USBSRC_STS_FAIL)
+			break;
+
+		msleep(50); /* Wait a bit before retrying */
+	} while (time_before(jiffies, timeout));
+
+	if (status != CHT_WC_USBSRC_STS_SUCCESS) {
+		if (ignore_errors)
+			return EXTCON_CHG_USB_SDP; /* Save fallback */
+
+		if (status == CHT_WC_USBSRC_STS_FAIL)
+			dev_warn(ext->dev, "Could not detect charger type\n");
+		else
+			dev_warn(ext->dev, "Timeout detecting charger type\n");
+		return EXTCON_CHG_USB_SDP; /* Save fallback */
+	}
+
+	usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT;
+	switch (usbsrc) {
+	default:
+		dev_warn(ext->dev,
+			"Unhandled charger type %d, defaulting to SDP\n",
+			 ret);
+		/* Fall through, treat as SDP */
+	case CHT_WC_USBSRC_TYPE_SDP:
+	case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN:
+	case CHT_WC_USBSRC_TYPE_OTHER:
+		return EXTCON_CHG_USB_SDP;
+	case CHT_WC_USBSRC_TYPE_CDP:
+		return EXTCON_CHG_USB_CDP;
+	case CHT_WC_USBSRC_TYPE_DCP:
+	case CHT_WC_USBSRC_TYPE_DCP_EXTPHY:
+	case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */
+		return EXTCON_CHG_USB_DCP;
+	case CHT_WC_USBSRC_TYPE_ACA:
+		return EXTCON_CHG_USB_ACA;
+	}
+}
+
+static void cht_wc_extcon_set_phymux(struct cht_wc_extcon_data *ext, u8 state)
+{
+	int ret;
+
+	ret = regmap_write(ext->regmap, CHT_WC_PHYCTRL, state);
+	if (ret)
+		dev_err(ext->dev, "Error writing phyctrl: %d\n", ret);
+}
+
+static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
+				       bool enable)
+{
+	int ret, val;
+
+	val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0;
+
+	/*
+	 * The 5V boost converter is enabled through a gpio on the PMIC, since
+	 * there currently is no gpio driver we access the gpio reg directly.
+	 */
+	ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO,
+				 CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val);
+	if (ret)
+		dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
+}
+
+/* Small helper to sync EXTCON_CHG_USB_SDP and EXTCON_USB state */
+static void cht_wc_extcon_set_state(struct cht_wc_extcon_data *ext,
+				    unsigned int cable, bool state)
+{
+	extcon_set_state_sync(ext->edev, cable, state);
+	if (cable == EXTCON_CHG_USB_SDP)
+		extcon_set_state_sync(ext->edev, EXTCON_USB, state);
+}
+
+static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
+{
+	int ret, pwrsrc_sts, id;
+	unsigned int cable = EXTCON_NONE;
+	/* Ignore errors in host mode, as the 5v boost converter is on then */
+	bool ignore_get_charger_errors = ext->usb_host;
+
+	ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
+	if (ret) {
+		dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
+		return;
+	}
+
+	id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
+	if (id == USB_ID_GND) {
+		/* The 5v boost causes a false VBUS / SDP detect, skip */
+		goto charger_det_done;
+	}
+
+	/* Plugged into a host/charger or not connected? */
+	if (!(pwrsrc_sts & CHT_WC_PWRSRC_VBUS)) {
+		/* Route D+ and D- to PMIC for future charger detection */
+		cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
+		goto set_state;
+	}
+
+	ret = cht_wc_extcon_get_charger(ext, ignore_get_charger_errors);
+	if (ret >= 0)
+		cable = ret;
+
+charger_det_done:
+	/* Route D+ and D- to SoC for the host or gadget controller */
+	cht_wc_extcon_set_phymux(ext, MUX_SEL_SOC);
+
+set_state:
+	if (cable != ext->previous_cable) {
+		cht_wc_extcon_set_state(ext, cable, true);
+		cht_wc_extcon_set_state(ext, ext->previous_cable, false);
+		ext->previous_cable = cable;
+	}
+
+	ext->usb_host = ((id == USB_ID_GND) || (id == USB_RID_A));
+	extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host);
+}
+
+static irqreturn_t cht_wc_extcon_isr(int irq, void *data)
+{
+	struct cht_wc_extcon_data *ext = data;
+	int ret, irqs;
+
+	ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_IRQ, &irqs);
+	if (ret) {
+		dev_err(ext->dev, "Error reading irqs: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	cht_wc_extcon_pwrsrc_event(ext);
+
+	ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ, irqs);
+	if (ret) {
+		dev_err(ext->dev, "Error writing irqs: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
+{
+	int ret, mask, val;
+
+	mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF_MASK;
+	val = enable ? mask : 0;
+	ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val);
+	if (ret)
+		dev_err(ext->dev, "Error setting sw control: %d\n", ret);
+
+	return ret;
+}
+
+static int cht_wc_extcon_probe(struct platform_device *pdev)
+{
+	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+	struct cht_wc_extcon_data *ext;
+	int irq, ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ext = devm_kzalloc(&pdev->dev, sizeof(*ext), GFP_KERNEL);
+	if (!ext)
+		return -ENOMEM;
+
+	ext->dev = &pdev->dev;
+	ext->regmap = pmic->regmap;
+	ext->previous_cable = EXTCON_NONE;
+
+	/* Initialize extcon device */
+	ext->edev = devm_extcon_dev_allocate(ext->dev, cht_wc_extcon_cables);
+	if (IS_ERR(ext->edev))
+		return PTR_ERR(ext->edev);
+
+	/*
+	 * When a host-cable is detected the BIOS enables an external 5v boost
+	 * converter to power connected devices there are 2 problems with this:
+	 * 1) This gets seen by the external battery charger as a valid Vbus
+	 *    supply and it then tries to feed Vsys from this creating a
+	 *    feedback loop which causes aprox. 300 mA extra battery drain
+	 *    (and unless we drive the external-charger-disable pin high it
+	 *    also tries to charge the battery causing even more feedback).
+	 * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
+	 * Since the external battery charger has its own 5v boost converter
+	 * which does not have these issues, we simply turn the separate
+	 * external 5v boost converter off and leave it off entirely.
+	 */
+	cht_wc_extcon_set_5v_boost(ext, false);
+
+	/* Enable sw control */
+	ret = cht_wc_extcon_sw_control(ext, true);
+	if (ret)
+		return ret;
+
+	/* Register extcon device */
+	ret = devm_extcon_dev_register(ext->dev, ext->edev);
+	if (ret) {
+		dev_err(ext->dev, "Error registering extcon device: %d\n", ret);
+		goto disable_sw_control;
+	}
+
+	/* Route D+ and D- to PMIC for initial charger detection */
+	cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
+
+	/* Get initial state */
+	cht_wc_extcon_pwrsrc_event(ext);
+
+	ret = devm_request_threaded_irq(ext->dev, irq, NULL, cht_wc_extcon_isr,
+					IRQF_ONESHOT, pdev->name, ext);
+	if (ret) {
+		dev_err(ext->dev, "Error requesting interrupt: %d\n", ret);
+		goto disable_sw_control;
+	}
+
+	/* Unmask irqs */
+	ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK,
+			   (int)~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_ID_GND |
+				  CHT_WC_PWRSRC_ID_FLOAT));
+	if (ret) {
+		dev_err(ext->dev, "Error writing irq-mask: %d\n", ret);
+		goto disable_sw_control;
+	}
+
+	platform_set_drvdata(pdev, ext);
+
+	return 0;
+
+disable_sw_control:
+	cht_wc_extcon_sw_control(ext, false);
+	return ret;
+}
+
+static int cht_wc_extcon_remove(struct platform_device *pdev)
+{
+	struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev);
+
+	cht_wc_extcon_sw_control(ext, false);
+
+	return 0;
+}
+
+static const struct platform_device_id cht_wc_extcon_table[] = {
+	{ .name = "cht_wcove_pwrsrc" },
+	{},
+};
+MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table);
+
+static struct platform_driver cht_wc_extcon_driver = {
+	.probe = cht_wc_extcon_probe,
+	.remove = cht_wc_extcon_remove,
+	.id_table = cht_wc_extcon_table,
+	.driver = {
+		.name = "cht_wcove_pwrsrc",
+	},
+};
+module_platform_driver(cht_wc_extcon_driver);
+
+MODULE_DESCRIPTION("Intel Cherrytrail Whiskey Cove PMIC extcon driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");

+ 6 - 0
drivers/extcon/extcon-palmas.c

@@ -413,6 +413,12 @@ static int palmas_usb_resume(struct device *dev)
 		if (palmas_usb->enable_gpio_id_detection)
 		if (palmas_usb->enable_gpio_id_detection)
 			disable_irq_wake(palmas_usb->gpio_id_irq);
 			disable_irq_wake(palmas_usb->gpio_id_irq);
 	}
 	}
+
+	/* check if GPIO states changed while suspend/resume */
+	if (palmas_usb->enable_gpio_vbus_detection)
+		palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
+	palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
+
 	return 0;
 	return 0;
 };
 };
 #endif
 #endif

+ 4 - 6
drivers/extcon/extcon-usb-gpio.c

@@ -26,7 +26,6 @@
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
-#include <linux/acpi.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/pinctrl/consumer.h>
 
 
 #define USB_GPIO_DEBOUNCE_MS	20	/* ms */
 #define USB_GPIO_DEBOUNCE_MS	20	/* ms */
@@ -111,7 +110,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
 	struct usb_extcon_info *info;
 	struct usb_extcon_info *info;
 	int ret;
 	int ret;
 
 
-	if (!np && !ACPI_HANDLE(dev))
+	if (!np)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@@ -195,7 +194,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	platform_set_drvdata(pdev, info);
 	platform_set_drvdata(pdev, info);
-	device_init_wakeup(dev, true);
+	device_set_wakeup_capable(&pdev->dev, true);
 
 
 	/* Perform initial detection */
 	/* Perform initial detection */
 	usb_extcon_detect_cable(&info->wq_detcable.work);
 	usb_extcon_detect_cable(&info->wq_detcable.work);
@@ -282,9 +281,8 @@ static int usb_extcon_resume(struct device *dev)
 	if (info->vbus_gpiod)
 	if (info->vbus_gpiod)
 		enable_irq(info->vbus_irq);
 		enable_irq(info->vbus_irq);
 
 
-	if (!device_may_wakeup(dev))
-		queue_delayed_work(system_power_efficient_wq,
-				   &info->wq_detcable, 0);
+	queue_delayed_work(system_power_efficient_wq,
+			   &info->wq_detcable, 0);
 
 
 	return ret;
 	return ret;
 }
 }

+ 1 - 21
drivers/extcon/extcon.c

@@ -230,9 +230,6 @@ struct extcon_cable {
 };
 };
 
 
 static struct class *extcon_class;
 static struct class *extcon_class;
-#if defined(CONFIG_ANDROID)
-static struct class_compat *switch_class;
-#endif /* CONFIG_ANDROID */
 
 
 static LIST_HEAD(extcon_dev_list);
 static LIST_HEAD(extcon_dev_list);
 static DEFINE_MUTEX(extcon_dev_list_lock);
 static DEFINE_MUTEX(extcon_dev_list_lock);
@@ -380,7 +377,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 	for (i = 0; i < edev->max_supported; i++) {
 	for (i = 0; i < edev->max_supported; i++) {
 		count += sprintf(buf + count, "%s=%d\n",
 		count += sprintf(buf + count, "%s=%d\n",
 				extcon_info[edev->supported_cable[i]].name,
 				extcon_info[edev->supported_cable[i]].name,
-				 !!(edev->state & (1 << i)));
+				 !!(edev->state & BIT(i)));
 	}
 	}
 
 
 	return count;
 	return count;
@@ -1032,12 +1029,6 @@ static int create_extcon_class(void)
 		if (IS_ERR(extcon_class))
 		if (IS_ERR(extcon_class))
 			return PTR_ERR(extcon_class);
 			return PTR_ERR(extcon_class);
 		extcon_class->dev_groups = extcon_groups;
 		extcon_class->dev_groups = extcon_groups;
-
-#if defined(CONFIG_ANDROID)
-		switch_class = class_compat_register("switch");
-		if (WARN(!switch_class, "cannot allocate"))
-			return -ENOMEM;
-#endif /* CONFIG_ANDROID */
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -1259,10 +1250,6 @@ int extcon_dev_register(struct extcon_dev *edev)
 		put_device(&edev->dev);
 		put_device(&edev->dev);
 		goto err_dev;
 		goto err_dev;
 	}
 	}
-#if defined(CONFIG_ANDROID)
-	if (switch_class)
-		ret = class_compat_create_link(switch_class, &edev->dev, NULL);
-#endif /* CONFIG_ANDROID */
 
 
 	spin_lock_init(&edev->lock);
 	spin_lock_init(&edev->lock);
 
 
@@ -1350,10 +1337,6 @@ void extcon_dev_unregister(struct extcon_dev *edev)
 		kfree(edev->cables);
 		kfree(edev->cables);
 	}
 	}
 
 
-#if defined(CONFIG_ANDROID)
-	if (switch_class)
-		class_compat_remove_link(switch_class, &edev->dev, NULL);
-#endif
 	put_device(&edev->dev);
 	put_device(&edev->dev);
 }
 }
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
@@ -1424,9 +1407,6 @@ module_init(extcon_class_init);
 
 
 static void __exit extcon_class_exit(void)
 static void __exit extcon_class_exit(void)
 {
 {
-#if defined(CONFIG_ANDROID)
-	class_compat_unregister(switch_class);
-#endif
 	class_destroy(extcon_class);
 	class_destroy(extcon_class);
 }
 }
 module_exit(extcon_class_exit);
 module_exit(extcon_class_exit);

+ 1 - 1
drivers/firewire/core-topology.c

@@ -124,7 +124,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
 	node->initiated_reset = SELF_ID_PHY_INITIATOR(sid);
 	node->initiated_reset = SELF_ID_PHY_INITIATOR(sid);
 	node->port_count = port_count;
 	node->port_count = port_count;
 
 
-	atomic_set(&node->ref_count, 1);
+	refcount_set(&node->ref_count, 1);
 	INIT_LIST_HEAD(&node->link);
 	INIT_LIST_HEAD(&node->link);
 
 
 	return node;
 	return node;

+ 4 - 4
drivers/firewire/core.h

@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/types.h>
 
 
-#include <linux/atomic.h>
+#include <linux/refcount.h>
 
 
 struct device;
 struct device;
 struct fw_card;
 struct fw_card;
@@ -184,7 +184,7 @@ struct fw_node {
 			 * local node to this node. */
 			 * local node to this node. */
 	u8 max_depth:4;	/* Maximum depth to any leaf node */
 	u8 max_depth:4;	/* Maximum depth to any leaf node */
 	u8 max_hops:4;	/* Max hops in this sub tree */
 	u8 max_hops:4;	/* Max hops in this sub tree */
-	atomic_t ref_count;
+	refcount_t ref_count;
 
 
 	/* For serializing node topology into a list. */
 	/* For serializing node topology into a list. */
 	struct list_head link;
 	struct list_head link;
@@ -197,14 +197,14 @@ struct fw_node {
 
 
 static inline struct fw_node *fw_node_get(struct fw_node *node)
 static inline struct fw_node *fw_node_get(struct fw_node *node)
 {
 {
-	atomic_inc(&node->ref_count);
+	refcount_inc(&node->ref_count);
 
 
 	return node;
 	return node;
 }
 }
 
 
 static inline void fw_node_put(struct fw_node *node)
 static inline void fw_node_put(struct fw_node *node)
 {
 {
-	if (atomic_dec_and_test(&node->ref_count))
+	if (refcount_dec_and_test(&node->ref_count))
 		kfree(node);
 		kfree(node);
 }
 }
 
 

+ 51 - 8
drivers/firmware/google/Kconfig

@@ -1,18 +1,16 @@
-config GOOGLE_FIRMWARE
+menuconfig GOOGLE_FIRMWARE
 	bool "Google Firmware Drivers"
 	bool "Google Firmware Drivers"
-	depends on X86
 	default n
 	default n
 	help
 	help
 	  These firmware drivers are used by Google's servers.  They are
 	  These firmware drivers are used by Google's servers.  They are
 	  only useful if you are working directly on one of their
 	  only useful if you are working directly on one of their
 	  proprietary servers.  If in doubt, say "N".
 	  proprietary servers.  If in doubt, say "N".
 
 
-menu "Google Firmware Drivers"
-	depends on GOOGLE_FIRMWARE
+if GOOGLE_FIRMWARE
 
 
 config GOOGLE_SMI
 config GOOGLE_SMI
 	tristate "SMI interface for Google platforms"
 	tristate "SMI interface for Google platforms"
-	depends on ACPI && DMI && EFI
+	depends on X86 && ACPI && DMI && EFI
 	select EFI_VARS
 	select EFI_VARS
 	help
 	help
 	  Say Y here if you want to enable SMI callbacks for Google
 	  Say Y here if you want to enable SMI callbacks for Google
@@ -20,12 +18,57 @@ config GOOGLE_SMI
 	  clearing the EFI event log and reading and writing NVRAM
 	  clearing the EFI event log and reading and writing NVRAM
 	  variables.
 	  variables.
 
 
+config GOOGLE_COREBOOT_TABLE
+	tristate
+	depends on GOOGLE_COREBOOT_TABLE_ACPI || GOOGLE_COREBOOT_TABLE_OF
+
+config GOOGLE_COREBOOT_TABLE_ACPI
+	tristate "Coreboot Table Access - ACPI"
+	depends on ACPI
+	select GOOGLE_COREBOOT_TABLE
+	help
+	  This option enables the coreboot_table module, which provides other
+	  firmware modules to access to the coreboot table. The coreboot table
+	  pointer is accessed through the ACPI "GOOGCB00" object.
+	  If unsure say N.
+
+config GOOGLE_COREBOOT_TABLE_OF
+	tristate "Coreboot Table Access - Device Tree"
+	depends on OF
+	select GOOGLE_COREBOOT_TABLE
+	help
+	  This option enable the coreboot_table module, which provide other
+	  firmware modules to access coreboot table. The coreboot table pointer
+	  is accessed through the device tree node /firmware/coreboot.
+	  If unsure say N.
+
 config GOOGLE_MEMCONSOLE
 config GOOGLE_MEMCONSOLE
-	tristate "Firmware Memory Console"
-	depends on DMI
+	tristate
+	depends on GOOGLE_MEMCONSOLE_X86_LEGACY || GOOGLE_MEMCONSOLE_COREBOOT
+
+config GOOGLE_MEMCONSOLE_X86_LEGACY
+	tristate "Firmware Memory Console - X86 Legacy support"
+	depends on X86 && ACPI && DMI
+	select GOOGLE_MEMCONSOLE
 	help
 	help
 	  This option enables the kernel to search for a firmware log in
 	  This option enables the kernel to search for a firmware log in
 	  the EBDA on Google servers.  If found, this log is exported to
 	  the EBDA on Google servers.  If found, this log is exported to
 	  userland in the file /sys/firmware/log.
 	  userland in the file /sys/firmware/log.
 
 
-endmenu
+config GOOGLE_MEMCONSOLE_COREBOOT
+	tristate "Firmware Memory Console"
+	depends on GOOGLE_COREBOOT_TABLE
+	select GOOGLE_MEMCONSOLE
+	help
+	  This option enables the kernel to search for a firmware log in
+	  the coreboot table.  If found, this log is exported to userland
+	  in the file /sys/firmware/log.
+
+config GOOGLE_VPD
+	tristate "Vital Product Data"
+	depends on GOOGLE_COREBOOT_TABLE
+	help
+	  This option enables the kernel to expose the content of Google VPD
+	  under /sys/firmware/vpd.
+
+endif # GOOGLE_FIRMWARE

+ 9 - 1
drivers/firmware/google/Makefile

@@ -1,3 +1,11 @@
 
 
 obj-$(CONFIG_GOOGLE_SMI)		+= gsmi.o
 obj-$(CONFIG_GOOGLE_SMI)		+= gsmi.o
-obj-$(CONFIG_GOOGLE_MEMCONSOLE)		+= memconsole.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE)        += coreboot_table.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI)   += coreboot_table-acpi.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF)     += coreboot_table-of.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE)            += memconsole.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT)   += memconsole-coreboot.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
+
+vpd-sysfs-y := vpd.o vpd_decode.o
+obj-$(CONFIG_GOOGLE_VPD)		+= vpd-sysfs.o

+ 88 - 0
drivers/firmware/google/coreboot_table-acpi.c

@@ -0,0 +1,88 @@
+/*
+ * coreboot_table-acpi.c
+ *
+ * Using ACPI to locate Coreboot table and provide coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "coreboot_table.h"
+
+static int coreboot_table_acpi_probe(struct platform_device *pdev)
+{
+	phys_addr_t phyaddr;
+	resource_size_t len;
+	struct coreboot_table_header __iomem *header = NULL;
+	struct resource *res;
+	void __iomem *ptr = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	len = resource_size(res);
+	if (!res->start || !len)
+		return -EINVAL;
+
+	phyaddr = res->start;
+	header = ioremap_cache(phyaddr, sizeof(*header));
+	if (header == NULL)
+		return -ENOMEM;
+
+	ptr = ioremap_cache(phyaddr,
+			    header->header_bytes + header->table_bytes);
+	iounmap(header);
+	if (!ptr)
+		return -ENOMEM;
+
+	return coreboot_table_init(ptr);
+}
+
+static int coreboot_table_acpi_remove(struct platform_device *pdev)
+{
+	return coreboot_table_exit();
+}
+
+static const struct acpi_device_id cros_coreboot_acpi_match[] = {
+	{ "GOOGCB00", 0 },
+	{ "BOOT0000", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
+
+static struct platform_driver coreboot_table_acpi_driver = {
+	.probe = coreboot_table_acpi_probe,
+	.remove = coreboot_table_acpi_remove,
+	.driver = {
+		.name = "coreboot_table_acpi",
+		.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
+	},
+};
+
+static int __init coreboot_table_acpi_init(void)
+{
+	return platform_driver_register(&coreboot_table_acpi_driver);
+}
+
+module_init(coreboot_table_acpi_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 82 - 0
drivers/firmware/google/coreboot_table-of.c

@@ -0,0 +1,82 @@
+/*
+ * coreboot_table-of.c
+ *
+ * Coreboot table access through open firmware.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "coreboot_table.h"
+
+static int coreboot_table_of_probe(struct platform_device *pdev)
+{
+	struct device_node *fw_dn = pdev->dev.of_node;
+	void __iomem *ptr;
+
+	ptr = of_iomap(fw_dn, 0);
+	of_node_put(fw_dn);
+	if (!ptr)
+		return -ENOMEM;
+
+	return coreboot_table_init(ptr);
+}
+
+static int coreboot_table_of_remove(struct platform_device *pdev)
+{
+	return coreboot_table_exit();
+}
+
+static const struct of_device_id coreboot_of_match[] = {
+	{ .compatible = "coreboot" },
+	{},
+};
+
+static struct platform_driver coreboot_table_of_driver = {
+	.probe = coreboot_table_of_probe,
+	.remove = coreboot_table_of_remove,
+	.driver = {
+		.name = "coreboot_table_of",
+		.of_match_table = coreboot_of_match,
+	},
+};
+
+static int __init platform_coreboot_table_of_init(void)
+{
+	struct platform_device *pdev;
+	struct device_node *of_node;
+
+	/* Limit device creation to the presence of /firmware/coreboot node */
+	of_node = of_find_node_by_path("/firmware/coreboot");
+	if (!of_node)
+		return -ENODEV;
+
+	if (!of_match_node(coreboot_of_match, of_node))
+		return -ENODEV;
+
+	pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL);
+	if (!pdev)
+		return -ENODEV;
+
+	return platform_driver_register(&coreboot_table_of_driver);
+}
+
+module_init(platform_coreboot_table_of_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 94 - 0
drivers/firmware/google/coreboot_table.c

@@ -0,0 +1,94 @@
+/*
+ * coreboot_table.c
+ *
+ * Module providing coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "coreboot_table.h"
+
+struct coreboot_table_entry {
+	u32 tag;
+	u32 size;
+};
+
+static struct coreboot_table_header __iomem *ptr_header;
+
+/*
+ * This function parses the coreboot table for an entry that contains the base
+ * address of the given entry tag. The coreboot table consists of a header
+ * directly followed by a number of small, variable-sized entries, which each
+ * contain an identifying tag and their length as the first two fields.
+ */
+int coreboot_table_find(int tag, void *data, size_t data_size)
+{
+	struct coreboot_table_header header;
+	struct coreboot_table_entry entry;
+	void *ptr_entry;
+	int i;
+
+	if (!ptr_header)
+		return -EPROBE_DEFER;
+
+	memcpy_fromio(&header, ptr_header, sizeof(header));
+
+	if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
+		pr_warn("coreboot_table: coreboot table missing or corrupt!\n");
+		return -ENODEV;
+	}
+
+	ptr_entry = (void *)ptr_header + header.header_bytes;
+
+	for (i = 0; i < header.table_entries; i++) {
+		memcpy_fromio(&entry, ptr_entry, sizeof(entry));
+		if (entry.tag == tag) {
+			if (data_size < entry.size)
+				return -EINVAL;
+
+			memcpy_fromio(data, ptr_entry, entry.size);
+
+			return 0;
+		}
+
+		ptr_entry += entry.size;
+	}
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL(coreboot_table_find);
+
+int coreboot_table_init(void __iomem *ptr)
+{
+	ptr_header = ptr;
+
+	return 0;
+}
+EXPORT_SYMBOL(coreboot_table_init);
+
+int coreboot_table_exit(void)
+{
+	if (ptr_header)
+		iounmap(ptr_header);
+
+	return 0;
+}
+EXPORT_SYMBOL(coreboot_table_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 50 - 0
drivers/firmware/google/coreboot_table.h

@@ -0,0 +1,50 @@
+/*
+ * coreboot_table.h
+ *
+ * Internal header for coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __COREBOOT_TABLE_H
+#define __COREBOOT_TABLE_H
+
+#include <linux/io.h>
+
+/* List of coreboot entry structures that is used */
+struct lb_cbmem_ref {
+	uint32_t tag;
+	uint32_t size;
+
+	uint64_t cbmem_addr;
+};
+
+/* Coreboot table header structure */
+struct coreboot_table_header {
+	char signature[4];
+	u32 header_bytes;
+	u32 header_checksum;
+	u32 table_bytes;
+	u32 table_checksum;
+	u32 table_entries;
+};
+
+/* Retrieve coreboot table entry with tag *tag* and copy it to data */
+int coreboot_table_find(int tag, void *data, size_t data_size);
+
+/* Initialize coreboot table module given a pointer to iomem */
+int coreboot_table_init(void __iomem *ptr);
+
+/* Cleanup coreboot table module */
+int coreboot_table_exit(void);
+
+#endif /* __COREBOOT_TABLE_H */

+ 109 - 0
drivers/firmware/google/memconsole-coreboot.c

@@ -0,0 +1,109 @@
+/*
+ * memconsole-coreboot.c
+ *
+ * Memory based BIOS console accessed through coreboot table.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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 "memconsole.h"
+#include "coreboot_table.h"
+
+#define CB_TAG_CBMEM_CONSOLE	0x17
+
+/* CBMEM firmware console log descriptor. */
+struct cbmem_cons {
+	u32 buffer_size;
+	u32 buffer_cursor;
+	u8  buffer_body[0];
+} __packed;
+
+static struct cbmem_cons __iomem *cbmem_console;
+
+static int memconsole_coreboot_init(phys_addr_t physaddr)
+{
+	struct cbmem_cons __iomem *tmp_cbmc;
+
+	tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB);
+
+	if (!tmp_cbmc)
+		return -ENOMEM;
+
+	cbmem_console = memremap(physaddr,
+				 tmp_cbmc->buffer_size + sizeof(*cbmem_console),
+				 MEMREMAP_WB);
+	memunmap(tmp_cbmc);
+
+	if (!cbmem_console)
+		return -ENOMEM;
+
+	memconsole_setup(cbmem_console->buffer_body,
+		min(cbmem_console->buffer_cursor, cbmem_console->buffer_size));
+
+	return 0;
+}
+
+static int memconsole_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct lb_cbmem_ref entry;
+
+	ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry));
+	if (ret)
+		return ret;
+
+	ret = memconsole_coreboot_init(entry.cbmem_addr);
+	if (ret)
+		return ret;
+
+	return memconsole_sysfs_init();
+}
+
+static int memconsole_remove(struct platform_device *pdev)
+{
+	memconsole_exit();
+
+	if (cbmem_console)
+		memunmap(cbmem_console);
+
+	return 0;
+}
+
+static struct platform_driver memconsole_driver = {
+	.probe = memconsole_probe,
+	.remove = memconsole_remove,
+	.driver = {
+		.name = "memconsole",
+	},
+};
+
+static int __init platform_memconsole_init(void)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("memconsole", -1, NULL, 0);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	platform_driver_register(&memconsole_driver);
+
+	return 0;
+}
+
+module_init(platform_memconsole_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 153 - 0
drivers/firmware/google/memconsole-x86-legacy.c

@@ -0,0 +1,153 @@
+/*
+ * memconsole-x86-legacy.c
+ *
+ * EBDA specific parts of the memory based BIOS console.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/dmi.h>
+#include <linux/mm.h>
+#include <asm/bios_ebda.h>
+#include <linux/acpi.h>
+
+#include "memconsole.h"
+
+#define BIOS_MEMCONSOLE_V1_MAGIC	0xDEADBABE
+#define BIOS_MEMCONSOLE_V2_MAGIC	(('M')|('C'<<8)|('O'<<16)|('N'<<24))
+
+struct biosmemcon_ebda {
+	u32 signature;
+	union {
+		struct {
+			u8  enabled;
+			u32 buffer_addr;
+			u16 start;
+			u16 end;
+			u16 num_chars;
+			u8  wrapped;
+		} __packed v1;
+		struct {
+			u32 buffer_addr;
+			/* Misdocumented as number of pages! */
+			u16 num_bytes;
+			u16 start;
+			u16 end;
+		} __packed v2;
+	};
+} __packed;
+
+static void found_v1_header(struct biosmemcon_ebda *hdr)
+{
+	pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n",
+		hdr);
+	pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num = %d\n",
+		hdr->v1.buffer_addr, hdr->v1.start,
+		hdr->v1.end, hdr->v1.num_chars);
+
+	memconsole_setup(phys_to_virt(hdr->v1.buffer_addr), hdr->v1.num_chars);
+}
+
+static void found_v2_header(struct biosmemcon_ebda *hdr)
+{
+	pr_info("memconsole: BIOS console v2 EBDA structure found at %p\n",
+		hdr);
+	pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num_bytes = %d\n",
+		hdr->v2.buffer_addr, hdr->v2.start,
+		hdr->v2.end, hdr->v2.num_bytes);
+
+	memconsole_setup(phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start),
+			 hdr->v2.end - hdr->v2.start);
+}
+
+/*
+ * Search through the EBDA for the BIOS Memory Console, and
+ * set the global variables to point to it.  Return true if found.
+ */
+static bool memconsole_ebda_init(void)
+{
+	unsigned int address;
+	size_t length, cur;
+
+	address = get_bios_ebda();
+	if (!address) {
+		pr_info("memconsole: BIOS EBDA non-existent.\n");
+		return false;
+	}
+
+	/* EBDA length is byte 0 of EBDA (in KB) */
+	length = *(u8 *)phys_to_virt(address);
+	length <<= 10; /* convert to bytes */
+
+	/*
+	 * Search through EBDA for BIOS memory console structure
+	 * note: signature is not necessarily dword-aligned
+	 */
+	for (cur = 0; cur < length; cur++) {
+		struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
+
+		/* memconsole v1 */
+		if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
+			found_v1_header(hdr);
+			return true;
+		}
+
+		/* memconsole v2 */
+		if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
+			found_v2_header(hdr);
+			return true;
+		}
+	}
+
+	pr_info("memconsole: BIOS console EBDA structure not found!\n");
+	return false;
+}
+
+static struct dmi_system_id memconsole_dmi_table[] __initdata = {
+	{
+		.ident = "Google Board",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
+		},
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
+
+static bool __init memconsole_find(void)
+{
+	if (!dmi_check_system(memconsole_dmi_table))
+		return false;
+
+	return memconsole_ebda_init();
+}
+
+static int __init memconsole_x86_init(void)
+{
+	if (!memconsole_find())
+		return -ENODEV;
+
+	return memconsole_sysfs_init();
+}
+
+static void __exit memconsole_x86_exit(void)
+{
+	memconsole_exit();
+}
+
+module_init(memconsole_x86_init);
+module_exit(memconsole_x86_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 23 - 132
drivers/firmware/google/memconsole.c

@@ -1,66 +1,36 @@
 /*
 /*
  * memconsole.c
  * memconsole.c
  *
  *
- * Infrastructure for importing the BIOS memory based console
- * into the kernel log ringbuffer.
+ * Architecture-independent parts of the memory based BIOS console.
  *
  *
- * Copyright 2010 Google Inc. All rights reserved.
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/ctype.h>
 #include <linux/init.h>
 #include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
 #include <linux/sysfs.h>
 #include <linux/sysfs.h>
 #include <linux/kobject.h>
 #include <linux/kobject.h>
 #include <linux/module.h>
 #include <linux/module.h>
-#include <linux/dmi.h>
-#include <linux/io.h>
-#include <asm/bios_ebda.h>
 
 
-#define BIOS_MEMCONSOLE_V1_MAGIC	0xDEADBABE
-#define BIOS_MEMCONSOLE_V2_MAGIC	(('M')|('C'<<8)|('O'<<16)|('N'<<24))
+#include "memconsole.h"
 
 
-struct biosmemcon_ebda {
-	u32 signature;
-	union {
-		struct {
-			u8  enabled;
-			u32 buffer_addr;
-			u16 start;
-			u16 end;
-			u16 num_chars;
-			u8  wrapped;
-		} __packed v1;
-		struct {
-			u32 buffer_addr;
-			/* Misdocumented as number of pages! */
-			u16 num_bytes;
-			u16 start;
-			u16 end;
-		} __packed v2;
-	};
-} __packed;
-
-static u32 memconsole_baseaddr;
+static char *memconsole_baseaddr;
 static size_t memconsole_length;
 static size_t memconsole_length;
 
 
 static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
 static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
 			       struct bin_attribute *bin_attr, char *buf,
 			       struct bin_attribute *bin_attr, char *buf,
 			       loff_t pos, size_t count)
 			       loff_t pos, size_t count)
 {
 {
-	char *memconsole;
-	ssize_t ret;
-
-	memconsole = ioremap_cache(memconsole_baseaddr, memconsole_length);
-	if (!memconsole) {
-		pr_err("memconsole: ioremap_cache failed\n");
-		return -ENOMEM;
-	}
-	ret = memory_read_from_buffer(buf, count, &pos, memconsole,
-				      memconsole_length);
-	iounmap(memconsole);
-	return ret;
+	return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
+				       memconsole_length);
 }
 }
 
 
 static struct bin_attribute memconsole_bin_attr = {
 static struct bin_attribute memconsole_bin_attr = {
@@ -68,104 +38,25 @@ static struct bin_attribute memconsole_bin_attr = {
 	.read = memconsole_read,
 	.read = memconsole_read,
 };
 };
 
 
-
-static void __init found_v1_header(struct biosmemcon_ebda *hdr)
-{
-	pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
-	pr_info("BIOS console buffer at 0x%.8x, "
-	       "start = %d, end = %d, num = %d\n",
-	       hdr->v1.buffer_addr, hdr->v1.start,
-	       hdr->v1.end, hdr->v1.num_chars);
-
-	memconsole_length = hdr->v1.num_chars;
-	memconsole_baseaddr = hdr->v1.buffer_addr;
-}
-
-static void __init found_v2_header(struct biosmemcon_ebda *hdr)
-{
-	pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
-	pr_info("BIOS console buffer at 0x%.8x, "
-	       "start = %d, end = %d, num_bytes = %d\n",
-	       hdr->v2.buffer_addr, hdr->v2.start,
-	       hdr->v2.end, hdr->v2.num_bytes);
-
-	memconsole_length = hdr->v2.end - hdr->v2.start;
-	memconsole_baseaddr = hdr->v2.buffer_addr + hdr->v2.start;
-}
-
-/*
- * Search through the EBDA for the BIOS Memory Console, and
- * set the global variables to point to it.  Return true if found.
- */
-static bool __init found_memconsole(void)
+void memconsole_setup(void *baseaddr, size_t length)
 {
 {
-	unsigned int address;
-	size_t length, cur;
-
-	address = get_bios_ebda();
-	if (!address) {
-		pr_info("BIOS EBDA non-existent.\n");
-		return false;
-	}
-
-	/* EBDA length is byte 0 of EBDA (in KB) */
-	length = *(u8 *)phys_to_virt(address);
-	length <<= 10; /* convert to bytes */
-
-	/*
-	 * Search through EBDA for BIOS memory console structure
-	 * note: signature is not necessarily dword-aligned
-	 */
-	for (cur = 0; cur < length; cur++) {
-		struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
-
-		/* memconsole v1 */
-		if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
-			found_v1_header(hdr);
-			return true;
-		}
-
-		/* memconsole v2 */
-		if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
-			found_v2_header(hdr);
-			return true;
-		}
-	}
-
-	pr_info("BIOS console EBDA structure not found!\n");
-	return false;
+	memconsole_baseaddr = baseaddr;
+	memconsole_length = length;
 }
 }
+EXPORT_SYMBOL(memconsole_setup);
 
 
-static struct dmi_system_id memconsole_dmi_table[] __initdata = {
-	{
-		.ident = "Google Board",
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
-		},
-	},
-	{}
-};
-MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
-
-static int __init memconsole_init(void)
+int memconsole_sysfs_init(void)
 {
 {
-	if (!dmi_check_system(memconsole_dmi_table))
-		return -ENODEV;
-
-	if (!found_memconsole())
-		return -ENODEV;
-
 	memconsole_bin_attr.size = memconsole_length;
 	memconsole_bin_attr.size = memconsole_length;
 	return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
 	return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
 }
 }
+EXPORT_SYMBOL(memconsole_sysfs_init);
 
 
-static void __exit memconsole_exit(void)
+void memconsole_exit(void)
 {
 {
 	sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
 	sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
 }
 }
-
-module_init(memconsole_init);
-module_exit(memconsole_exit);
+EXPORT_SYMBOL(memconsole_exit);
 
 
 MODULE_AUTHOR("Google, Inc.");
 MODULE_AUTHOR("Google, Inc.");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 43 - 0
drivers/firmware/google/memconsole.h

@@ -0,0 +1,43 @@
+/*
+ * memconsole.h
+ *
+ * Internal headers of the memory based BIOS console.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H
+#define __FIRMWARE_GOOGLE_MEMCONSOLE_H
+
+/*
+ * memconsole_setup
+ *
+ * Initialize the memory console from raw (virtual) base
+ * address and length.
+ */
+void memconsole_setup(void *baseaddr, size_t length);
+
+/*
+ * memconsole_sysfs_init
+ *
+ * Update memory console length and create binary file
+ * for firmware object.
+ */
+int memconsole_sysfs_init(void);
+
+/* memconsole_exit
+ *
+ * Unmap the console buffer.
+ */
+void memconsole_exit(void);
+
+#endif /* __FIRMWARE_GOOGLE_MEMCONSOLE_H */

+ 332 - 0
drivers/firmware/google/vpd.c

@@ -0,0 +1,332 @@
+/*
+ * vpd.c
+ *
+ * Driver for exporting VPD content to sysfs.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/ctype.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "coreboot_table.h"
+#include "vpd_decode.h"
+
+#define CB_TAG_VPD      0x2c
+#define VPD_CBMEM_MAGIC 0x43524f53
+
+static struct kobject *vpd_kobj;
+
+struct vpd_cbmem {
+	u32 magic;
+	u32 version;
+	u32 ro_size;
+	u32 rw_size;
+	u8  blob[0];
+};
+
+struct vpd_section {
+	bool enabled;
+	const char *name;
+	char *raw_name;                /* the string name_raw */
+	struct kobject *kobj;          /* vpd/name directory */
+	char *baseaddr;
+	struct bin_attribute bin_attr; /* vpd/name_raw bin_attribute */
+	struct list_head attribs;      /* key/value in vpd_attrib_info list */
+};
+
+struct vpd_attrib_info {
+	char *key;
+	const char *value;
+	struct bin_attribute bin_attr;
+	struct list_head list;
+};
+
+static struct vpd_section ro_vpd;
+static struct vpd_section rw_vpd;
+
+static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
+			       struct bin_attribute *bin_attr, char *buf,
+			       loff_t pos, size_t count)
+{
+	struct vpd_attrib_info *info = bin_attr->private;
+
+	return memory_read_from_buffer(buf, count, &pos, info->value,
+				       info->bin_attr.size);
+}
+
+/*
+ * vpd_section_check_key_name()
+ *
+ * The VPD specification supports only [a-zA-Z0-9_]+ characters in key names but
+ * old firmware versions may have entries like "S/N" which are problematic when
+ * exporting them as sysfs attributes. These keys present in old firmwares are
+ * ignored.
+ *
+ * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
+ *
+ * @key: The key name to check
+ * @key_len: key name length
+ */
+static int vpd_section_check_key_name(const u8 *key, s32 key_len)
+{
+	int c;
+
+	while (key_len-- > 0) {
+		c = *key++;
+
+		if (!isalnum(c) && c != '_')
+			return VPD_FAIL;
+	}
+
+	return VPD_OK;
+}
+
+static int vpd_section_attrib_add(const u8 *key, s32 key_len,
+				  const u8 *value, s32 value_len,
+				  void *arg)
+{
+	int ret;
+	struct vpd_section *sec = arg;
+	struct vpd_attrib_info *info;
+
+	/*
+	 * Return VPD_OK immediately to decode next entry if the current key
+	 * name contains invalid characters.
+	 */
+	if (vpd_section_check_key_name(key, key_len) != VPD_OK)
+		return VPD_OK;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	info->key = kzalloc(key_len + 1, GFP_KERNEL);
+	if (!info->key)
+		return -ENOMEM;
+
+	memcpy(info->key, key, key_len);
+
+	sysfs_bin_attr_init(&info->bin_attr);
+	info->bin_attr.attr.name = info->key;
+	info->bin_attr.attr.mode = 0444;
+	info->bin_attr.size = value_len;
+	info->bin_attr.read = vpd_attrib_read;
+	info->bin_attr.private = info;
+
+	info->value = value;
+
+	INIT_LIST_HEAD(&info->list);
+	list_add_tail(&info->list, &sec->attribs);
+
+	ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
+	if (ret) {
+		kfree(info->key);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void vpd_section_attrib_destroy(struct vpd_section *sec)
+{
+	struct vpd_attrib_info *info;
+	struct vpd_attrib_info *temp;
+
+	list_for_each_entry_safe(info, temp, &sec->attribs, list) {
+		kfree(info->key);
+		sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
+		kfree(info);
+	}
+}
+
+static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
+				struct bin_attribute *bin_attr, char *buf,
+				loff_t pos, size_t count)
+{
+	struct vpd_section *sec = bin_attr->private;
+
+	return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
+				       sec->bin_attr.size);
+}
+
+static int vpd_section_create_attribs(struct vpd_section *sec)
+{
+	s32 consumed;
+	int ret;
+
+	consumed = 0;
+	do {
+		ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
+					&consumed, vpd_section_attrib_add, sec);
+	} while (ret == VPD_OK);
+
+	return 0;
+}
+
+static int vpd_section_init(const char *name, struct vpd_section *sec,
+			    phys_addr_t physaddr, size_t size)
+{
+	int ret;
+	int raw_len;
+
+	sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
+	if (!sec->baseaddr)
+		return -ENOMEM;
+
+	sec->name = name;
+
+	/* We want to export the raw partion with name ${name}_raw */
+	raw_len = strlen(name) + 5;
+	sec->raw_name = kzalloc(raw_len, GFP_KERNEL);
+	strncpy(sec->raw_name, name, raw_len);
+	strncat(sec->raw_name, "_raw", raw_len);
+
+	sysfs_bin_attr_init(&sec->bin_attr);
+	sec->bin_attr.attr.name = sec->raw_name;
+	sec->bin_attr.attr.mode = 0444;
+	sec->bin_attr.size = size;
+	sec->bin_attr.read = vpd_section_read;
+	sec->bin_attr.private = sec;
+
+	ret = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
+	if (ret)
+		goto free_sec;
+
+	sec->kobj = kobject_create_and_add(name, vpd_kobj);
+	if (!sec->kobj) {
+		ret = -EINVAL;
+		goto sysfs_remove;
+	}
+
+	INIT_LIST_HEAD(&sec->attribs);
+	vpd_section_create_attribs(sec);
+
+	sec->enabled = true;
+
+	return 0;
+
+sysfs_remove:
+	sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
+
+free_sec:
+	kfree(sec->raw_name);
+	iounmap(sec->baseaddr);
+
+	return ret;
+}
+
+static int vpd_section_destroy(struct vpd_section *sec)
+{
+	if (sec->enabled) {
+		vpd_section_attrib_destroy(sec);
+		kobject_del(sec->kobj);
+		sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
+		kfree(sec->raw_name);
+		iounmap(sec->baseaddr);
+	}
+
+	return 0;
+}
+
+static int vpd_sections_init(phys_addr_t physaddr)
+{
+	struct vpd_cbmem __iomem *temp;
+	struct vpd_cbmem header;
+	int ret = 0;
+
+	temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
+	if (!temp)
+		return -ENOMEM;
+
+	memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem));
+	iounmap(temp);
+
+	if (header.magic != VPD_CBMEM_MAGIC)
+		return -ENODEV;
+
+	if (header.ro_size) {
+		ret = vpd_section_init("ro", &ro_vpd,
+				       physaddr + sizeof(struct vpd_cbmem),
+				       header.ro_size);
+		if (ret)
+			return ret;
+	}
+
+	if (header.rw_size) {
+		ret = vpd_section_init("rw", &rw_vpd,
+				       physaddr + sizeof(struct vpd_cbmem) +
+				       header.ro_size, header.rw_size);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int vpd_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct lb_cbmem_ref entry;
+
+	ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry));
+	if (ret)
+		return ret;
+
+	return vpd_sections_init(entry.cbmem_addr);
+}
+
+static struct platform_driver vpd_driver = {
+	.probe = vpd_probe,
+	.driver = {
+		.name = "vpd",
+	},
+};
+
+static int __init vpd_platform_init(void)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("vpd", -1, NULL, 0);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
+	if (!vpd_kobj)
+		return -ENOMEM;
+
+	memset(&ro_vpd, 0, sizeof(ro_vpd));
+	memset(&rw_vpd, 0, sizeof(rw_vpd));
+
+	platform_driver_register(&vpd_driver);
+
+	return 0;
+}
+
+static void __exit vpd_platform_exit(void)
+{
+	vpd_section_destroy(&ro_vpd);
+	vpd_section_destroy(&rw_vpd);
+	kobject_del(vpd_kobj);
+}
+
+module_init(vpd_platform_init);
+module_exit(vpd_platform_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");

+ 99 - 0
drivers/firmware/google/vpd_decode.c

@@ -0,0 +1,99 @@
+/*
+ * vpd_decode.c
+ *
+ * Google VPD decoding routines.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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/export.h>
+
+#include "vpd_decode.h"
+
+static int vpd_decode_len(const s32 max_len, const u8 *in,
+			  s32 *length, s32 *decoded_len)
+{
+	u8 more;
+	int i = 0;
+
+	if (!length || !decoded_len)
+		return VPD_FAIL;
+
+	*length = 0;
+	do {
+		if (i >= max_len)
+			return VPD_FAIL;
+
+		more = in[i] & 0x80;
+		*length <<= 7;
+		*length |= in[i] & 0x7f;
+		++i;
+	} while (more);
+
+	*decoded_len = i;
+
+	return VPD_OK;
+}
+
+int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
+		      vpd_decode_callback callback, void *callback_arg)
+{
+	int type;
+	int res;
+	s32 key_len;
+	s32 value_len;
+	s32 decoded_len;
+	const u8 *key;
+	const u8 *value;
+
+	/* type */
+	if (*consumed >= max_len)
+		return VPD_FAIL;
+
+	type = input_buf[*consumed];
+
+	switch (type) {
+	case VPD_TYPE_INFO:
+	case VPD_TYPE_STRING:
+		(*consumed)++;
+
+		/* key */
+		res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
+				     &key_len, &decoded_len);
+		if (res != VPD_OK || *consumed + decoded_len >= max_len)
+			return VPD_FAIL;
+
+		*consumed += decoded_len;
+		key = &input_buf[*consumed];
+		*consumed += key_len;
+
+		/* value */
+		res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
+				     &value_len, &decoded_len);
+		if (res != VPD_OK || *consumed + decoded_len > max_len)
+			return VPD_FAIL;
+
+		*consumed += decoded_len;
+		value = &input_buf[*consumed];
+		*consumed += value_len;
+
+		if (type == VPD_TYPE_STRING)
+			return callback(key, key_len, value, value_len,
+					callback_arg);
+		break;
+
+	default:
+		return VPD_FAIL;
+	}
+
+	return VPD_OK;
+}

+ 58 - 0
drivers/firmware/google/vpd_decode.h

@@ -0,0 +1,58 @@
+/*
+ * vpd_decode.h
+ *
+ * Google VPD decoding routines.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __VPD_DECODE_H
+#define __VPD_DECODE_H
+
+#include <linux/types.h>
+
+enum {
+	VPD_OK = 0,
+	VPD_FAIL,
+};
+
+enum {
+	VPD_TYPE_TERMINATOR = 0,
+	VPD_TYPE_STRING,
+	VPD_TYPE_INFO                = 0xfe,
+	VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
+};
+
+/* Callback for vpd_decode_string to invoke. */
+typedef int vpd_decode_callback(const u8 *key, s32 key_len,
+				const u8 *value, s32 value_len,
+				void *arg);
+
+/*
+ * vpd_decode_string
+ *
+ * Given the encoded string, this function invokes callback with extracted
+ * (key, value). The *consumed will be plused the number of bytes consumed in
+ * this function.
+ *
+ * The input_buf points to the first byte of the input buffer.
+ *
+ * The *consumed starts from 0, which is actually the next byte to be decoded.
+ * It can be non-zero to be used in multiple calls.
+ *
+ * If one entry is successfully decoded, sends it to callback and returns the
+ * result.
+ */
+int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
+		      vpd_decode_callback callback, void *callback_arg);
+
+#endif  /* __VPD_DECODE_H */

+ 42 - 0
drivers/fpga/Kconfig

@@ -20,6 +20,12 @@ config FPGA_REGION
 	  FPGA Regions allow loading FPGA images under control of
 	  FPGA Regions allow loading FPGA images under control of
 	  the Device Tree.
 	  the Device Tree.
 
 
+config FPGA_MGR_ICE40_SPI
+	tristate "Lattice iCE40 SPI"
+	depends on OF && SPI
+	help
+	  FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
+
 config FPGA_MGR_SOCFPGA
 config FPGA_MGR_SOCFPGA
 	tristate "Altera SOCFPGA FPGA Manager"
 	tristate "Altera SOCFPGA FPGA Manager"
 	depends on ARCH_SOCFPGA || COMPILE_TEST
 	depends on ARCH_SOCFPGA || COMPILE_TEST
@@ -33,6 +39,20 @@ config FPGA_MGR_SOCFPGA_A10
 	help
 	help
 	  FPGA manager driver support for Altera Arria10 SoCFPGA.
 	  FPGA manager driver support for Altera Arria10 SoCFPGA.
 
 
+config FPGA_MGR_TS73XX
+	tristate "Technologic Systems TS-73xx SBC FPGA Manager"
+	depends on ARCH_EP93XX && MACH_TS72XX
+	help
+	  FPGA manager driver support for the Altera Cyclone II FPGA
+	  present on the TS-73xx SBC boards.
+
+config FPGA_MGR_XILINX_SPI
+	tristate "Xilinx Configuration over Slave Serial (SPI)"
+	depends on SPI
+	help
+	  FPGA manager driver support for Xilinx FPGA configuration
+	  over slave serial interface.
+
 config FPGA_MGR_ZYNQ_FPGA
 config FPGA_MGR_ZYNQ_FPGA
 	tristate "Xilinx Zynq FPGA"
 	tristate "Xilinx Zynq FPGA"
 	depends on ARCH_ZYNQ || COMPILE_TEST
 	depends on ARCH_ZYNQ || COMPILE_TEST
@@ -63,6 +83,28 @@ config ALTERA_FREEZE_BRIDGE
 	  isolate one region of the FPGA from the busses while that
 	  isolate one region of the FPGA from the busses while that
 	  region is being reprogrammed.
 	  region is being reprogrammed.
 
 
+config ALTERA_PR_IP_CORE
+        tristate "Altera Partial Reconfiguration IP Core"
+        help
+          Core driver support for Altera Partial Reconfiguration IP component
+
+config ALTERA_PR_IP_CORE_PLAT
+	tristate "Platform support of Altera Partial Reconfiguration IP Core"
+	depends on ALTERA_PR_IP_CORE && OF && HAS_IOMEM
+	help
+	  Platform driver support for Altera Partial Reconfiguration IP
+	  component
+
+config XILINX_PR_DECOUPLER
+	tristate "Xilinx LogiCORE PR Decoupler"
+	depends on FPGA_BRIDGE
+	depends on HAS_IOMEM
+	help
+	  Say Y to enable drivers for Xilinx LogiCORE PR Decoupler.
+	  The PR Decoupler exists in the FPGA fabric to isolate one
+	  region of the FPGA from the busses while that region is
+	  being reprogrammed during partial reconfig.
+
 endif # FPGA
 endif # FPGA
 
 
 endmenu
 endmenu

+ 6 - 0
drivers/fpga/Makefile

@@ -6,14 +6,20 @@
 obj-$(CONFIG_FPGA)			+= fpga-mgr.o
 obj-$(CONFIG_FPGA)			+= fpga-mgr.o
 
 
 # FPGA Manager Drivers
 # FPGA Manager Drivers
+obj-$(CONFIG_FPGA_MGR_ICE40_SPI)	+= ice40-spi.o
 obj-$(CONFIG_FPGA_MGR_SOCFPGA)		+= socfpga.o
 obj-$(CONFIG_FPGA_MGR_SOCFPGA)		+= socfpga.o
 obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10)	+= socfpga-a10.o
 obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10)	+= socfpga-a10.o
+obj-$(CONFIG_FPGA_MGR_TS73XX)		+= ts73xx-fpga.o
+obj-$(CONFIG_FPGA_MGR_XILINX_SPI)	+= xilinx-spi.o
 obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
 obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
+obj-$(CONFIG_ALTERA_PR_IP_CORE)         += altera-pr-ip-core.o
+obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT)    += altera-pr-ip-core-plat.o
 
 
 # FPGA Bridge Drivers
 # FPGA Bridge Drivers
 obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
 obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
 obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
 obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
 obj-$(CONFIG_ALTERA_FREEZE_BRIDGE)	+= altera-freeze-bridge.o
 obj-$(CONFIG_ALTERA_FREEZE_BRIDGE)	+= altera-freeze-bridge.o
+obj-$(CONFIG_XILINX_PR_DECOUPLER)	+= xilinx-pr-decoupler.o
 
 
 # High Level Interfaces
 # High Level Interfaces
 obj-$(CONFIG_FPGA_REGION)		+= fpga-region.o
 obj-$(CONFIG_FPGA_REGION)		+= fpga-region.o

+ 20 - 12
drivers/fpga/altera-freeze-bridge.c

@@ -28,6 +28,7 @@
 #define FREEZE_CSR_REG_VERSION			12
 #define FREEZE_CSR_REG_VERSION			12
 
 
 #define FREEZE_CSR_SUPPORTED_VERSION		2
 #define FREEZE_CSR_SUPPORTED_VERSION		2
+#define FREEZE_CSR_OFFICIAL_VERSION		0xad000003
 
 
 #define FREEZE_CSR_STATUS_FREEZE_REQ_DONE	BIT(0)
 #define FREEZE_CSR_STATUS_FREEZE_REQ_DONE	BIT(0)
 #define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE	BIT(1)
 #define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE	BIT(1)
@@ -203,7 +204,7 @@ static int altera_freeze_br_enable_show(struct fpga_bridge *bridge)
 	return priv->enable;
 	return priv->enable;
 }
 }
 
 
-static struct fpga_bridge_ops altera_freeze_br_br_ops = {
+static const struct fpga_bridge_ops altera_freeze_br_br_ops = {
 	.enable_set = altera_freeze_br_enable_set,
 	.enable_set = altera_freeze_br_enable_set,
 	.enable_show = altera_freeze_br_enable_show,
 	.enable_show = altera_freeze_br_enable_show,
 };
 };
@@ -218,6 +219,7 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
 {
 {
 	struct device *dev = &pdev->dev;
 	struct device *dev = &pdev->dev;
 	struct device_node *np = pdev->dev.of_node;
 	struct device_node *np = pdev->dev.of_node;
+	void __iomem *base_addr;
 	struct altera_freeze_br_data *priv;
 	struct altera_freeze_br_data *priv;
 	struct resource *res;
 	struct resource *res;
 	u32 status, revision;
 	u32 status, revision;
@@ -225,26 +227,32 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
 	if (!np)
 	if (!np)
 		return -ENODEV;
 		return -ENODEV;
 
 
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base_addr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base_addr))
+		return PTR_ERR(base_addr);
+
+	revision = readl(base_addr + FREEZE_CSR_REG_VERSION);
+	if ((revision != FREEZE_CSR_SUPPORTED_VERSION) &&
+	    (revision != FREEZE_CSR_OFFICIAL_VERSION)) {
+		dev_err(dev,
+			"%s unexpected revision 0x%x != 0x%x != 0x%x\n",
+			__func__, revision, FREEZE_CSR_SUPPORTED_VERSION,
+			FREEZE_CSR_OFFICIAL_VERSION);
+		return -EINVAL;
+	}
+
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 	if (!priv)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	priv->dev = dev;
 	priv->dev = dev;
 
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	priv->base_addr = devm_ioremap_resource(dev, res);
-	if (IS_ERR(priv->base_addr))
-		return PTR_ERR(priv->base_addr);
-
-	status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+	status = readl(base_addr + FREEZE_CSR_STATUS_OFFSET);
 	if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
 	if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
 		priv->enable = 1;
 		priv->enable = 1;
 
 
-	revision = readl(priv->base_addr + FREEZE_CSR_REG_VERSION);
-	if (revision != FREEZE_CSR_SUPPORTED_VERSION)
-		dev_warn(dev,
-			 "%s Freeze Controller unexpected revision %d != %d\n",
-			 __func__, revision, FREEZE_CSR_SUPPORTED_VERSION);
+	priv->base_addr = base_addr;
 
 
 	return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
 	return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
 				    &altera_freeze_br_br_ops, priv);
 				    &altera_freeze_br_br_ops, priv);

+ 9 - 6
drivers/fpga/altera-hps2fpga.c

@@ -181,15 +181,18 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
 				 (enable ? "enabling" : "disabling"));
 				 (enable ? "enabling" : "disabling"));
 
 
 			ret = _alt_hps2fpga_enable_set(priv, enable);
 			ret = _alt_hps2fpga_enable_set(priv, enable);
-			if (ret) {
-				fpga_bridge_unregister(&pdev->dev);
-				return ret;
-			}
+			if (ret)
+				goto err;
 		}
 		}
 	}
 	}
 
 
-	return fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
-				    priv);
+	ret = fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
+				   priv);
+err:
+	if (ret)
+		clk_disable_unprepare(priv->clk);
+
+	return ret;
 }
 }
 
 
 static int alt_fpga_bridge_remove(struct platform_device *pdev)
 static int alt_fpga_bridge_remove(struct platform_device *pdev)

+ 68 - 0
drivers/fpga/altera-pr-ip-core-plat.c

@@ -0,0 +1,68 @@
+/*
+ * Driver for Altera Partial Reconfiguration IP Core
+ *
+ * Copyright (C) 2016-2017 Intel Corporation
+ *
+ * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
+ *  by Alan Tull <atull@opensource.altera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/fpga/altera-pr-ip-core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+static int alt_pr_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *reg_base;
+	struct resource *res;
+
+	/* First mmio base is for register access */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	reg_base = devm_ioremap_resource(dev, res);
+
+	if (IS_ERR(reg_base))
+		return PTR_ERR(reg_base);
+
+	return alt_pr_register(dev, reg_base);
+}
+
+static int alt_pr_platform_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	return alt_pr_unregister(dev);
+}
+
+static const struct of_device_id alt_pr_of_match[] = {
+	{ .compatible = "altr,a10-pr-ip", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, alt_pr_of_match);
+
+static struct platform_driver alt_pr_platform_driver = {
+	.probe = alt_pr_platform_probe,
+	.remove = alt_pr_platform_remove,
+	.driver = {
+		.name	= "alt_a10_pr_ip",
+		.of_match_table = alt_pr_of_match,
+	},
+};
+
+module_platform_driver(alt_pr_platform_driver);
+MODULE_AUTHOR("Matthew Gerlach <matthew.gerlach@linux.intel.com>");
+MODULE_DESCRIPTION("Altera Partial Reconfiguration IP Platform Driver");
+MODULE_LICENSE("GPL v2");

+ 220 - 0
drivers/fpga/altera-pr-ip-core.c

@@ -0,0 +1,220 @@
+/*
+ * Driver for Altera Partial Reconfiguration IP Core
+ *
+ * Copyright (C) 2016-2017 Intel Corporation
+ *
+ * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
+ *  by Alan Tull <atull@opensource.altera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/delay.h>
+#include <linux/fpga/altera-pr-ip-core.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/module.h>
+
+#define ALT_PR_DATA_OFST		0x00
+#define ALT_PR_CSR_OFST			0x04
+
+#define ALT_PR_CSR_PR_START		BIT(0)
+#define ALT_PR_CSR_STATUS_SFT		2
+#define ALT_PR_CSR_STATUS_MSK		(7 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_NRESET	(0 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_PR_ERR	(1 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_CRC_ERR	(2 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_BAD_BITS	(3 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_PR_IN_PROG	(4 << ALT_PR_CSR_STATUS_SFT)
+#define ALT_PR_CSR_STATUS_PR_SUCCESS	(5 << ALT_PR_CSR_STATUS_SFT)
+
+struct alt_pr_priv {
+	void __iomem *reg_base;
+};
+
+static enum fpga_mgr_states alt_pr_fpga_state(struct fpga_manager *mgr)
+{
+	struct alt_pr_priv *priv = mgr->priv;
+	const char *err = "unknown";
+	enum fpga_mgr_states ret = FPGA_MGR_STATE_UNKNOWN;
+	u32 val;
+
+	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
+
+	val &= ALT_PR_CSR_STATUS_MSK;
+
+	switch (val) {
+	case ALT_PR_CSR_STATUS_NRESET:
+		return FPGA_MGR_STATE_RESET;
+
+	case ALT_PR_CSR_STATUS_PR_ERR:
+		err = "pr error";
+		ret = FPGA_MGR_STATE_WRITE_ERR;
+		break;
+
+	case ALT_PR_CSR_STATUS_CRC_ERR:
+		err = "crc error";
+		ret = FPGA_MGR_STATE_WRITE_ERR;
+		break;
+
+	case ALT_PR_CSR_STATUS_BAD_BITS:
+		err = "bad bits";
+		ret = FPGA_MGR_STATE_WRITE_ERR;
+		break;
+
+	case ALT_PR_CSR_STATUS_PR_IN_PROG:
+		return FPGA_MGR_STATE_WRITE;
+
+	case ALT_PR_CSR_STATUS_PR_SUCCESS:
+		return FPGA_MGR_STATE_OPERATING;
+
+	default:
+		break;
+	}
+
+	dev_err(&mgr->dev, "encountered error code %d (%s) in %s()\n",
+		val, err, __func__);
+	return ret;
+}
+
+static int alt_pr_fpga_write_init(struct fpga_manager *mgr,
+				  struct fpga_image_info *info,
+				  const char *buf, size_t count)
+{
+	struct alt_pr_priv *priv = mgr->priv;
+	u32 val;
+
+	if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+		dev_err(&mgr->dev, "%s Partial Reconfiguration flag not set\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
+
+	if (val & ALT_PR_CSR_PR_START) {
+		dev_err(&mgr->dev,
+			"%s Partial Reconfiguration already started\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	writel(val | ALT_PR_CSR_PR_START, priv->reg_base + ALT_PR_CSR_OFST);
+
+	return 0;
+}
+
+static int alt_pr_fpga_write(struct fpga_manager *mgr, const char *buf,
+			     size_t count)
+{
+	struct alt_pr_priv *priv = mgr->priv;
+	u32 *buffer_32 = (u32 *)buf;
+	size_t i = 0;
+
+	if (count <= 0)
+		return -EINVAL;
+
+	/* Write out the complete 32-bit chunks */
+	while (count >= sizeof(u32)) {
+		writel(buffer_32[i++], priv->reg_base);
+		count -= sizeof(u32);
+	}
+
+	/* Write out remaining non 32-bit chunks */
+	switch (count) {
+	case 3:
+		writel(buffer_32[i++] & 0x00ffffff, priv->reg_base);
+		break;
+	case 2:
+		writel(buffer_32[i++] & 0x0000ffff, priv->reg_base);
+		break;
+	case 1:
+		writel(buffer_32[i++] & 0x000000ff, priv->reg_base);
+		break;
+	case 0:
+		break;
+	default:
+		/* This will never happen */
+		return -EFAULT;
+	}
+
+	if (alt_pr_fpga_state(mgr) == FPGA_MGR_STATE_WRITE_ERR)
+		return -EIO;
+
+	return 0;
+}
+
+static int alt_pr_fpga_write_complete(struct fpga_manager *mgr,
+				      struct fpga_image_info *info)
+{
+	u32 i = 0;
+
+	do {
+		switch (alt_pr_fpga_state(mgr)) {
+		case FPGA_MGR_STATE_WRITE_ERR:
+			return -EIO;
+
+		case FPGA_MGR_STATE_OPERATING:
+			dev_info(&mgr->dev,
+				 "successful partial reconfiguration\n");
+			return 0;
+
+		default:
+			break;
+		}
+		udelay(1);
+	} while (info->config_complete_timeout_us > i++);
+
+	dev_err(&mgr->dev, "timed out waiting for write to complete\n");
+	return -ETIMEDOUT;
+}
+
+static const struct fpga_manager_ops alt_pr_ops = {
+	.state = alt_pr_fpga_state,
+	.write_init = alt_pr_fpga_write_init,
+	.write = alt_pr_fpga_write,
+	.write_complete = alt_pr_fpga_write_complete,
+};
+
+int alt_pr_register(struct device *dev, void __iomem *reg_base)
+{
+	struct alt_pr_priv *priv;
+	u32 val;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->reg_base = reg_base;
+
+	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
+
+	dev_dbg(dev, "%s status=%d start=%d\n", __func__,
+		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
+		(int)(val & ALT_PR_CSR_PR_START));
+
+	return fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
+}
+EXPORT_SYMBOL_GPL(alt_pr_register);
+
+int alt_pr_unregister(struct device *dev)
+{
+	dev_dbg(dev, "%s\n", __func__);
+
+	fpga_mgr_unregister(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(alt_pr_unregister);
+
+MODULE_AUTHOR("Matthew Gerlach <matthew.gerlach@linux.intel.com>");
+MODULE_DESCRIPTION("Altera Partial Reconfiguration IP Core");
+MODULE_LICENSE("GPL v2");

+ 5 - 12
drivers/fpga/fpga-bridge.c

@@ -27,7 +27,7 @@ static DEFINE_IDA(fpga_bridge_ida);
 static struct class *fpga_bridge_class;
 static struct class *fpga_bridge_class;
 
 
 /* Lock for adding/removing bridges to linked lists*/
 /* Lock for adding/removing bridges to linked lists*/
-spinlock_t bridge_list_lock;
+static spinlock_t bridge_list_lock;
 
 
 static int fpga_bridge_of_node_match(struct device *dev, const void *data)
 static int fpga_bridge_of_node_match(struct device *dev, const void *data)
 {
 {
@@ -146,11 +146,9 @@ EXPORT_SYMBOL_GPL(fpga_bridge_put);
 int fpga_bridges_enable(struct list_head *bridge_list)
 int fpga_bridges_enable(struct list_head *bridge_list)
 {
 {
 	struct fpga_bridge *bridge;
 	struct fpga_bridge *bridge;
-	struct list_head *node;
 	int ret;
 	int ret;
 
 
-	list_for_each(node, bridge_list) {
-		bridge = list_entry(node, struct fpga_bridge, node);
+	list_for_each_entry(bridge, bridge_list, node) {
 		ret = fpga_bridge_enable(bridge);
 		ret = fpga_bridge_enable(bridge);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
@@ -172,11 +170,9 @@ EXPORT_SYMBOL_GPL(fpga_bridges_enable);
 int fpga_bridges_disable(struct list_head *bridge_list)
 int fpga_bridges_disable(struct list_head *bridge_list)
 {
 {
 	struct fpga_bridge *bridge;
 	struct fpga_bridge *bridge;
-	struct list_head *node;
 	int ret;
 	int ret;
 
 
-	list_for_each(node, bridge_list) {
-		bridge = list_entry(node, struct fpga_bridge, node);
+	list_for_each_entry(bridge, bridge_list, node) {
 		ret = fpga_bridge_disable(bridge);
 		ret = fpga_bridge_disable(bridge);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
@@ -196,13 +192,10 @@ EXPORT_SYMBOL_GPL(fpga_bridges_disable);
  */
  */
 void fpga_bridges_put(struct list_head *bridge_list)
 void fpga_bridges_put(struct list_head *bridge_list)
 {
 {
-	struct fpga_bridge *bridge;
-	struct list_head *node, *next;
+	struct fpga_bridge *bridge, *next;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	list_for_each_safe(node, next, bridge_list) {
-		bridge = list_entry(node, struct fpga_bridge, node);
-
+	list_for_each_entry_safe(bridge, next, bridge_list, node) {
 		fpga_bridge_put(bridge);
 		fpga_bridge_put(bridge);
 
 
 		spin_lock_irqsave(&bridge_list_lock, flags);
 		spin_lock_irqsave(&bridge_list_lock, flags);

+ 1 - 1
drivers/fpga/fpga-mgr.c

@@ -361,7 +361,7 @@ static struct attribute *fpga_mgr_attrs[] = {
 };
 };
 ATTRIBUTE_GROUPS(fpga_mgr);
 ATTRIBUTE_GROUPS(fpga_mgr);
 
 
-struct fpga_manager *__fpga_mgr_get(struct device *dev)
+static struct fpga_manager *__fpga_mgr_get(struct device *dev)
 {
 {
 	struct fpga_manager *mgr;
 	struct fpga_manager *mgr;
 	int ret = -ENODEV;
 	int ret = -ENODEV;

+ 12 - 3
drivers/fpga/fpga-region.c

@@ -245,7 +245,8 @@ static int fpga_region_program_fpga(struct fpga_region *region,
 	mgr = fpga_region_get_manager(region);
 	mgr = fpga_region_get_manager(region);
 	if (IS_ERR(mgr)) {
 	if (IS_ERR(mgr)) {
 		pr_err("failed to get fpga region manager\n");
 		pr_err("failed to get fpga region manager\n");
-		return PTR_ERR(mgr);
+		ret = PTR_ERR(mgr);
+		goto err_put_region;
 	}
 	}
 
 
 	ret = fpga_region_get_bridges(region, overlay);
 	ret = fpga_region_get_bridges(region, overlay);
@@ -281,6 +282,7 @@ err_put_br:
 	fpga_bridges_put(&region->bridge_list);
 	fpga_bridges_put(&region->bridge_list);
 err_put_mgr:
 err_put_mgr:
 	fpga_mgr_put(mgr);
 	fpga_mgr_put(mgr);
+err_put_region:
 	fpga_region_put(region);
 	fpga_region_put(region);
 
 
 	return ret;
 	return ret;
@@ -337,8 +339,9 @@ static int child_regions_with_firmware(struct device_node *overlay)
  * The overlay must add either firmware-name or external-fpga-config property
  * The overlay must add either firmware-name or external-fpga-config property
  * to the FPGA Region.
  * to the FPGA Region.
  *
  *
- *   firmware-name        : program the FPGA
- *   external-fpga-config : FPGA is already programmed
+ *   firmware-name         : program the FPGA
+ *   external-fpga-config  : FPGA is already programmed
+ *   encrypted-fpga-config : FPGA bitstream is encrypted
  *
  *
  * The overlay can add other FPGA regions, but child FPGA regions cannot have a
  * The overlay can add other FPGA regions, but child FPGA regions cannot have a
  * firmware-name property since those regions don't exist yet.
  * firmware-name property since those regions don't exist yet.
@@ -373,6 +376,9 @@ static int fpga_region_notify_pre_apply(struct fpga_region *region,
 	if (of_property_read_bool(nd->overlay, "external-fpga-config"))
 	if (of_property_read_bool(nd->overlay, "external-fpga-config"))
 		info->flags |= FPGA_MGR_EXTERNAL_CONFIG;
 		info->flags |= FPGA_MGR_EXTERNAL_CONFIG;
 
 
+	if (of_property_read_bool(nd->overlay, "encrypted-fpga-config"))
+		info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM;
+
 	of_property_read_string(nd->overlay, "firmware-name", &firmware_name);
 	of_property_read_string(nd->overlay, "firmware-name", &firmware_name);
 
 
 	of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us",
 	of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us",
@@ -381,6 +387,9 @@ static int fpga_region_notify_pre_apply(struct fpga_region *region,
 	of_property_read_u32(nd->overlay, "region-freeze-timeout-us",
 	of_property_read_u32(nd->overlay, "region-freeze-timeout-us",
 			     &info->disable_timeout_us);
 			     &info->disable_timeout_us);
 
 
+	of_property_read_u32(nd->overlay, "config-complete-timeout-us",
+			     &info->config_complete_timeout_us);
+
 	/* If FPGA was externally programmed, don't specify firmware */
 	/* If FPGA was externally programmed, don't specify firmware */
 	if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) {
 	if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) {
 		pr_err("error: specified firmware and external-fpga-config");
 		pr_err("error: specified firmware and external-fpga-config");

+ 207 - 0
drivers/fpga/ice40-spi.c

@@ -0,0 +1,207 @@
+/*
+ * FPGA Manager Driver for Lattice iCE40.
+ *
+ *  Copyright (c) 2016 Joel Holdsworth
+ *
+ * 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; version 2 of the License.
+ *
+ * This driver adds support to the FPGA manager for configuring the SRAM of
+ * Lattice iCE40 FPGAs through slave SPI.
+ */
+
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/stringify.h>
+
+#define ICE40_SPI_MAX_SPEED 25000000 /* Hz */
+#define ICE40_SPI_MIN_SPEED 1000000 /* Hz */
+
+#define ICE40_SPI_RESET_DELAY 1 /* us (>200ns) */
+#define ICE40_SPI_HOUSEKEEPING_DELAY 1200 /* us */
+
+#define ICE40_SPI_NUM_ACTIVATION_BYTES DIV_ROUND_UP(49, 8)
+
+struct ice40_fpga_priv {
+	struct spi_device *dev;
+	struct gpio_desc *reset;
+	struct gpio_desc *cdone;
+};
+
+static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
+{
+	struct ice40_fpga_priv *priv = mgr->priv;
+
+	return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
+		FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ice40_fpga_ops_write_init(struct fpga_manager *mgr,
+				     struct fpga_image_info *info,
+				     const char *buf, size_t count)
+{
+	struct ice40_fpga_priv *priv = mgr->priv;
+	struct spi_device *dev = priv->dev;
+	struct spi_message message;
+	struct spi_transfer assert_cs_then_reset_delay = {
+		.cs_change   = 1,
+		.delay_usecs = ICE40_SPI_RESET_DELAY
+	};
+	struct spi_transfer housekeeping_delay_then_release_cs = {
+		.delay_usecs = ICE40_SPI_HOUSEKEEPING_DELAY
+	};
+	int ret;
+
+	if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+		dev_err(&dev->dev,
+			"Partial reconfiguration is not supported\n");
+		return -ENOTSUPP;
+	}
+
+	/* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
+	spi_bus_lock(dev->master);
+
+	gpiod_set_value(priv->reset, 1);
+
+	spi_message_init(&message);
+	spi_message_add_tail(&assert_cs_then_reset_delay, &message);
+	ret = spi_sync_locked(dev, &message);
+
+	/* Come out of reset */
+	gpiod_set_value(priv->reset, 0);
+
+	/* Abort if the chip-select failed */
+	if (ret)
+		goto fail;
+
+	/* Check CDONE is de-asserted i.e. the FPGA is reset */
+	if (gpiod_get_value(priv->cdone)) {
+		dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
+		ret = -EIO;
+		goto fail;
+	}
+
+	/* Wait for the housekeeping to complete, and release SS_B */
+	spi_message_init(&message);
+	spi_message_add_tail(&housekeeping_delay_then_release_cs, &message);
+	ret = spi_sync_locked(dev, &message);
+
+fail:
+	spi_bus_unlock(dev->master);
+
+	return ret;
+}
+
+static int ice40_fpga_ops_write(struct fpga_manager *mgr,
+				const char *buf, size_t count)
+{
+	struct ice40_fpga_priv *priv = mgr->priv;
+
+	return spi_write(priv->dev, buf, count);
+}
+
+static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr,
+					 struct fpga_image_info *info)
+{
+	struct ice40_fpga_priv *priv = mgr->priv;
+	struct spi_device *dev = priv->dev;
+	const u8 padding[ICE40_SPI_NUM_ACTIVATION_BYTES] = {0};
+
+	/* Check CDONE is asserted */
+	if (!gpiod_get_value(priv->cdone)) {
+		dev_err(&dev->dev,
+			"CDONE was not asserted after firmware transfer\n");
+		return -EIO;
+	}
+
+	/* Send of zero-padding to activate the firmware */
+	return spi_write(dev, padding, sizeof(padding));
+}
+
+static const struct fpga_manager_ops ice40_fpga_ops = {
+	.state = ice40_fpga_ops_state,
+	.write_init = ice40_fpga_ops_write_init,
+	.write = ice40_fpga_ops_write,
+	.write_complete = ice40_fpga_ops_write_complete,
+};
+
+static int ice40_fpga_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct ice40_fpga_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = spi;
+
+	/* Check board setup data. */
+	if (spi->max_speed_hz > ICE40_SPI_MAX_SPEED) {
+		dev_err(dev, "SPI speed is too high, maximum speed is "
+			__stringify(ICE40_SPI_MAX_SPEED) "\n");
+		return -EINVAL;
+	}
+
+	if (spi->max_speed_hz < ICE40_SPI_MIN_SPEED) {
+		dev_err(dev, "SPI speed is too low, minimum speed is "
+			__stringify(ICE40_SPI_MIN_SPEED) "\n");
+		return -EINVAL;
+	}
+
+	if (spi->mode & SPI_CPHA) {
+		dev_err(dev, "Bad SPI mode, CPHA not supported\n");
+		return -EINVAL;
+	}
+
+	/* Set up the GPIOs */
+	priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
+	if (IS_ERR(priv->cdone)) {
+		ret = PTR_ERR(priv->cdone);
+		dev_err(dev, "Failed to get CDONE GPIO: %d\n", ret);
+		return ret;
+	}
+
+	priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->reset)) {
+		ret = PTR_ERR(priv->reset);
+		dev_err(dev, "Failed to get CRESET_B GPIO: %d\n", ret);
+		return ret;
+	}
+
+	/* Register with the FPGA manager */
+	return fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
+				 &ice40_fpga_ops, priv);
+}
+
+static int ice40_fpga_remove(struct spi_device *spi)
+{
+	fpga_mgr_unregister(&spi->dev);
+	return 0;
+}
+
+static const struct of_device_id ice40_fpga_of_match[] = {
+	{ .compatible = "lattice,ice40-fpga-mgr", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
+
+static struct spi_driver ice40_fpga_driver = {
+	.probe = ice40_fpga_probe,
+	.remove = ice40_fpga_remove,
+	.driver = {
+		.name = "ice40spi",
+		.of_match_table = of_match_ptr(ice40_fpga_of_match),
+	},
+};
+
+module_spi_driver(ice40_fpga_driver);
+
+MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
+MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
+MODULE_LICENSE("GPL v2");

+ 156 - 0
drivers/fpga/ts73xx-fpga.c

@@ -0,0 +1,156 @@
+/*
+ * Technologic Systems TS-73xx SBC FPGA loader
+ *
+ * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
+ *
+ * FPGA Manager Driver for the on-board Altera Cyclone II FPGA found on
+ * TS-7300, heavily based on load_fpga.c in their vendor tree.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/iopoll.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#define TS73XX_FPGA_DATA_REG		0
+#define TS73XX_FPGA_CONFIG_REG		1
+
+#define TS73XX_FPGA_WRITE_DONE		0x1
+#define TS73XX_FPGA_WRITE_DONE_TIMEOUT	1000	/* us */
+#define TS73XX_FPGA_RESET		0x2
+#define TS73XX_FPGA_RESET_LOW_DELAY	30	/* us */
+#define TS73XX_FPGA_RESET_HIGH_DELAY	80	/* us */
+#define TS73XX_FPGA_LOAD_OK		0x4
+#define TS73XX_FPGA_CONFIG_LOAD		0x8
+
+struct ts73xx_fpga_priv {
+	void __iomem	*io_base;
+	struct device	*dev;
+};
+
+static enum fpga_mgr_states ts73xx_fpga_state(struct fpga_manager *mgr)
+{
+	return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ts73xx_fpga_write_init(struct fpga_manager *mgr,
+				  struct fpga_image_info *info,
+				  const char *buf, size_t count)
+{
+	struct ts73xx_fpga_priv *priv = mgr->priv;
+
+	/* Reset the FPGA */
+	writeb(0, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+	udelay(TS73XX_FPGA_RESET_LOW_DELAY);
+	writeb(TS73XX_FPGA_RESET, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+	udelay(TS73XX_FPGA_RESET_HIGH_DELAY);
+
+	return 0;
+}
+
+static int ts73xx_fpga_write(struct fpga_manager *mgr, const char *buf,
+			     size_t count)
+{
+	struct ts73xx_fpga_priv *priv = mgr->priv;
+	size_t i = 0;
+	int ret;
+	u8 reg;
+
+	while (count--) {
+		ret = readb_poll_timeout(priv->io_base + TS73XX_FPGA_CONFIG_REG,
+					 reg, !(reg & TS73XX_FPGA_WRITE_DONE),
+					 1, TS73XX_FPGA_WRITE_DONE_TIMEOUT);
+		if (ret < 0)
+			return ret;
+
+		writeb(buf[i], priv->io_base + TS73XX_FPGA_DATA_REG);
+		i++;
+	}
+
+	return 0;
+}
+
+static int ts73xx_fpga_write_complete(struct fpga_manager *mgr,
+				      struct fpga_image_info *info)
+{
+	struct ts73xx_fpga_priv *priv = mgr->priv;
+	u8 reg;
+
+	usleep_range(1000, 2000);
+	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+	reg |= TS73XX_FPGA_CONFIG_LOAD;
+	writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+
+	usleep_range(1000, 2000);
+	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+	reg &= ~TS73XX_FPGA_CONFIG_LOAD;
+	writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+
+	reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+	if ((reg & TS73XX_FPGA_LOAD_OK) != TS73XX_FPGA_LOAD_OK)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static const struct fpga_manager_ops ts73xx_fpga_ops = {
+	.state		= ts73xx_fpga_state,
+	.write_init	= ts73xx_fpga_write_init,
+	.write		= ts73xx_fpga_write,
+	.write_complete	= ts73xx_fpga_write_complete,
+};
+
+static int ts73xx_fpga_probe(struct platform_device *pdev)
+{
+	struct device *kdev = &pdev->dev;
+	struct ts73xx_fpga_priv *priv;
+	struct resource *res;
+
+	priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = kdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->io_base = devm_ioremap_resource(kdev, res);
+	if (IS_ERR(priv->io_base)) {
+		dev_err(kdev, "unable to remap registers\n");
+		return PTR_ERR(priv->io_base);
+	}
+
+	return fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
+				 &ts73xx_fpga_ops, priv);
+}
+
+static int ts73xx_fpga_remove(struct platform_device *pdev)
+{
+	fpga_mgr_unregister(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver ts73xx_fpga_driver = {
+	.driver	= {
+		.name	= "ts73xx-fpga-mgr",
+	},
+	.probe	= ts73xx_fpga_probe,
+	.remove	= ts73xx_fpga_remove,
+};
+module_platform_driver(ts73xx_fpga_driver);
+
+MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
+MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
+MODULE_LICENSE("GPL v2");

+ 161 - 0
drivers/fpga/xilinx-pr-decoupler.c

@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ * Copyright (c) 2017, Xilix Inc
+ *
+ * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
+ * Decoupler IP Core.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/fpga/fpga-bridge.h>
+
+#define CTRL_CMD_DECOUPLE	BIT(0)
+#define CTRL_CMD_COUPLE		0
+#define CTRL_OFFSET		0
+
+struct xlnx_pr_decoupler_data {
+	void __iomem *io_base;
+	struct clk *clk;
+};
+
+static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
+					   u32 offset, u32 val)
+{
+	writel(val, d->io_base + offset);
+}
+
+static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
+					u32 offset)
+{
+	return readl(d->io_base + offset);
+}
+
+static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+	int err;
+	struct xlnx_pr_decoupler_data *priv = bridge->priv;
+
+	err = clk_enable(priv->clk);
+	if (err)
+		return err;
+
+	if (enable)
+		xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
+	else
+		xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
+
+	clk_disable(priv->clk);
+
+	return 0;
+}
+
+static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
+{
+	const struct xlnx_pr_decoupler_data *priv = bridge->priv;
+	u32 status;
+	int err;
+
+	err = clk_enable(priv->clk);
+	if (err)
+		return err;
+
+	status = readl(priv->io_base);
+
+	clk_disable(priv->clk);
+
+	return !status;
+}
+
+static struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
+	.enable_set = xlnx_pr_decoupler_enable_set,
+	.enable_show = xlnx_pr_decoupler_enable_show,
+};
+
+static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
+	{ .compatible = "xlnx,pr-decoupler-1.00", },
+	{ .compatible = "xlnx,pr-decoupler", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
+
+static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
+{
+	struct xlnx_pr_decoupler_data *priv;
+	int err;
+	struct resource *res;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->io_base))
+		return PTR_ERR(priv->io_base);
+
+	priv->clk = devm_clk_get(&pdev->dev, "aclk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "input clock not found\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return err;
+	}
+
+	clk_disable(priv->clk);
+
+	err = fpga_bridge_register(&pdev->dev, "Xilinx PR Decoupler",
+				   &xlnx_pr_decoupler_br_ops, priv);
+
+	if (err) {
+		dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler");
+		clk_unprepare(priv->clk);
+		return err;
+	}
+
+	return 0;
+}
+
+static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
+{
+	struct fpga_bridge *bridge = platform_get_drvdata(pdev);
+	struct xlnx_pr_decoupler_data *p = bridge->priv;
+
+	fpga_bridge_unregister(&pdev->dev);
+
+	clk_unprepare(p->clk);
+
+	return 0;
+}
+
+static struct platform_driver xlnx_pr_decoupler_driver = {
+	.probe = xlnx_pr_decoupler_probe,
+	.remove = xlnx_pr_decoupler_remove,
+	.driver = {
+		.name = "xlnx_pr_decoupler",
+		.of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
+	},
+};
+
+module_platform_driver(xlnx_pr_decoupler_driver);
+
+MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
+MODULE_LICENSE("GPL v2");

+ 198 - 0
drivers/fpga/xilinx-spi.c

@@ -0,0 +1,198 @@
+/*
+ * Xilinx Spartan6 Slave Serial SPI Driver
+ *
+ * Copyright (C) 2017 DENX Software Engineering
+ *
+ * Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Manage Xilinx FPGA firmware that is loaded over SPI using
+ * the slave serial configuration interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/sizes.h>
+
+struct xilinx_spi_conf {
+	struct spi_device *spi;
+	struct gpio_desc *prog_b;
+	struct gpio_desc *done;
+};
+
+static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
+{
+	struct xilinx_spi_conf *conf = mgr->priv;
+
+	if (!gpiod_get_value(conf->done))
+		return FPGA_MGR_STATE_RESET;
+
+	return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int xilinx_spi_write_init(struct fpga_manager *mgr,
+				 struct fpga_image_info *info,
+				 const char *buf, size_t count)
+{
+	struct xilinx_spi_conf *conf = mgr->priv;
+	const size_t prog_latency_7500us = 7500;
+	const size_t prog_pulse_1us = 1;
+
+	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+		dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
+		return -EINVAL;
+	}
+
+	gpiod_set_value(conf->prog_b, 1);
+
+	udelay(prog_pulse_1us); /* min is 500 ns */
+
+	gpiod_set_value(conf->prog_b, 0);
+
+	if (gpiod_get_value(conf->done)) {
+		dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
+		return -EIO;
+	}
+
+	/* program latency */
+	usleep_range(prog_latency_7500us, prog_latency_7500us + 100);
+	return 0;
+}
+
+static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
+			    size_t count)
+{
+	struct xilinx_spi_conf *conf = mgr->priv;
+	const char *fw_data = buf;
+	const char *fw_data_end = fw_data + count;
+
+	while (fw_data < fw_data_end) {
+		size_t remaining, stride;
+		int ret;
+
+		remaining = fw_data_end - fw_data;
+		stride = min_t(size_t, remaining, SZ_4K);
+
+		ret = spi_write(conf->spi, fw_data, stride);
+		if (ret) {
+			dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
+				ret);
+			return ret;
+		}
+		fw_data += stride;
+	}
+
+	return 0;
+}
+
+static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
+{
+	struct spi_device *spi = conf->spi;
+	const u8 din_data[1] = { 0xff };
+	int ret;
+
+	ret = spi_write(conf->spi, din_data, sizeof(din_data));
+	if (ret)
+		dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
+
+	return ret;
+}
+
+static int xilinx_spi_write_complete(struct fpga_manager *mgr,
+				     struct fpga_image_info *info)
+{
+	struct xilinx_spi_conf *conf = mgr->priv;
+	unsigned long timeout;
+	int ret;
+
+	if (gpiod_get_value(conf->done))
+		return xilinx_spi_apply_cclk_cycles(conf);
+
+	timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
+
+	while (time_before(jiffies, timeout)) {
+
+		ret = xilinx_spi_apply_cclk_cycles(conf);
+		if (ret)
+			return ret;
+
+		if (gpiod_get_value(conf->done))
+			return xilinx_spi_apply_cclk_cycles(conf);
+	}
+
+	dev_err(&mgr->dev, "Timeout after config data transfer.\n");
+	return -ETIMEDOUT;
+}
+
+static const struct fpga_manager_ops xilinx_spi_ops = {
+	.state = xilinx_spi_state,
+	.write_init = xilinx_spi_write_init,
+	.write = xilinx_spi_write,
+	.write_complete = xilinx_spi_write_complete,
+};
+
+static int xilinx_spi_probe(struct spi_device *spi)
+{
+	struct xilinx_spi_conf *conf;
+
+	conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
+	if (!conf)
+		return -ENOMEM;
+
+	conf->spi = spi;
+
+	/* PROGRAM_B is active low */
+	conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
+	if (IS_ERR(conf->prog_b)) {
+		dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n",
+			PTR_ERR(conf->prog_b));
+		return PTR_ERR(conf->prog_b);
+	}
+
+	conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
+	if (IS_ERR(conf->done)) {
+		dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n",
+			PTR_ERR(conf->done));
+		return PTR_ERR(conf->done);
+	}
+
+	return fpga_mgr_register(&spi->dev, "Xilinx Slave Serial FPGA Manager",
+				 &xilinx_spi_ops, conf);
+}
+
+static int xilinx_spi_remove(struct spi_device *spi)
+{
+	fpga_mgr_unregister(&spi->dev);
+
+	return 0;
+}
+
+static const struct of_device_id xlnx_spi_of_match[] = {
+	{ .compatible = "xlnx,fpga-slave-serial", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);
+
+static struct spi_driver xilinx_slave_spi_driver = {
+	.driver = {
+		.name = "xlnx-slave-spi",
+		.of_match_table = of_match_ptr(xlnx_spi_of_match),
+	},
+	.probe = xilinx_spi_probe,
+	.remove = xilinx_spi_remove,
+};
+
+module_spi_driver(xilinx_slave_spi_driver)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI");

+ 25 - 3
drivers/fpga/zynq-fpga.c

@@ -72,6 +72,10 @@
 #define CTRL_PCAP_PR_MASK		BIT(27)
 #define CTRL_PCAP_PR_MASK		BIT(27)
 /* Enable PCAP */
 /* Enable PCAP */
 #define CTRL_PCAP_MODE_MASK		BIT(26)
 #define CTRL_PCAP_MODE_MASK		BIT(26)
+/* Lower rate to allow decrypt on the fly */
+#define CTRL_PCAP_RATE_EN_MASK		BIT(25)
+/* System booted in secure mode */
+#define CTRL_SEC_EN_MASK		BIT(7)
 
 
 /* Miscellaneous Control Register bit definitions */
 /* Miscellaneous Control Register bit definitions */
 /* Internal PCAP loopback */
 /* Internal PCAP loopback */
@@ -266,6 +270,17 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
 	if (err)
 	if (err)
 		return err;
 		return err;
 
 
+	/* check if bitstream is encrypted & and system's still secure */
+	if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) {
+		ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
+		if (!(ctrl & CTRL_SEC_EN_MASK)) {
+			dev_err(&mgr->dev,
+				"System not secure, can't use crypted bitstreams\n");
+			err = -EINVAL;
+			goto out_err;
+		}
+	}
+
 	/* don't globally reset PL if we're doing partial reconfig */
 	/* don't globally reset PL if we're doing partial reconfig */
 	if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
 	if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
 		if (!zynq_fpga_has_sync(buf, count)) {
 		if (!zynq_fpga_has_sync(buf, count)) {
@@ -337,12 +352,19 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
 
 
 	/* set configuration register with following options:
 	/* set configuration register with following options:
 	 * - enable PCAP interface
 	 * - enable PCAP interface
-	 * - set throughput for maximum speed
+	 * - set throughput for maximum speed (if bistream not crypted)
 	 * - set CPU in user mode
 	 * - set CPU in user mode
 	 */
 	 */
 	ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
 	ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
-	zynq_fpga_write(priv, CTRL_OFFSET,
-			(CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK | ctrl));
+	if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM)
+		zynq_fpga_write(priv, CTRL_OFFSET,
+				(CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK
+				 | CTRL_PCAP_RATE_EN_MASK | ctrl));
+	else
+		zynq_fpga_write(priv, CTRL_OFFSET,
+				(CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK
+				 | ctrl));
+
 
 
 	/* We expect that the command queue is empty right now. */
 	/* We expect that the command queue is empty right now. */
 	status = zynq_fpga_read(priv, STATUS_OFFSET);
 	status = zynq_fpga_read(priv, STATUS_OFFSET);

+ 8 - 15
drivers/gpio/gpiolib.c

@@ -1035,18 +1035,14 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
 
 
 	cdev_init(&gdev->chrdev, &gpio_fileops);
 	cdev_init(&gdev->chrdev, &gpio_fileops);
 	gdev->chrdev.owner = THIS_MODULE;
 	gdev->chrdev.owner = THIS_MODULE;
-	gdev->chrdev.kobj.parent = &gdev->dev.kobj;
 	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
 	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
-	status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
-	if (status < 0)
-		chip_warn(gdev->chip, "failed to add char device %d:%d\n",
-			  MAJOR(gpio_devt), gdev->id);
-	else
-		chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
-			 MAJOR(gpio_devt), gdev->id);
-	status = device_add(&gdev->dev);
+
+	status = cdev_device_add(&gdev->chrdev, &gdev->dev);
 	if (status)
 	if (status)
-		goto err_remove_chardev;
+		return status;
+
+	chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
+		 MAJOR(gpio_devt), gdev->id);
 
 
 	status = gpiochip_sysfs_register(gdev);
 	status = gpiochip_sysfs_register(gdev);
 	if (status)
 	if (status)
@@ -1061,9 +1057,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
 	return 0;
 	return 0;
 
 
 err_remove_device:
 err_remove_device:
-	device_del(&gdev->dev);
-err_remove_chardev:
-	cdev_del(&gdev->chrdev);
+	cdev_device_del(&gdev->chrdev, &gdev->dev);
 	return status;
 	return status;
 }
 }
 
 
@@ -1347,8 +1341,7 @@ void gpiochip_remove(struct gpio_chip *chip)
 	 * be removed, else it will be dangling until the last user is
 	 * be removed, else it will be dangling until the last user is
 	 * gone.
 	 * gone.
 	 */
 	 */
-	cdev_del(&gdev->chrdev);
-	device_del(&gdev->dev);
+	cdev_device_del(&gdev->chrdev, &gdev->dev);
 	put_device(&gdev->dev);
 	put_device(&gdev->dev);
 }
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);
 EXPORT_SYMBOL_GPL(gpiochip_remove);

+ 5 - 5
drivers/hv/channel.c

@@ -333,7 +333,7 @@ static int create_gpadl_header(void *kbuffer, u32 size,
 			 * Gpadl is u32 and we are using a pointer which could
 			 * Gpadl is u32 and we are using a pointer which could
 			 * be 64-bit
 			 * be 64-bit
 			 * This is governed by the guest/host protocol and
 			 * This is governed by the guest/host protocol and
-			 * so the hypervisor gurantees that this is ok.
+			 * so the hypervisor guarantees that this is ok.
 			 */
 			 */
 			for (i = 0; i < pfncurr; i++)
 			for (i = 0; i < pfncurr; i++)
 				gpadl_body->pfn[i] = slow_virt_to_phys(
 				gpadl_body->pfn[i] = slow_virt_to_phys(
@@ -380,7 +380,7 @@ nomem:
 }
 }
 
 
 /*
 /*
- * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer
+ * vmbus_establish_gpadl - Establish a GPADL for the specified buffer
  *
  *
  * @channel: a channel
  * @channel: a channel
  * @kbuffer: from kmalloc or vmalloc
  * @kbuffer: from kmalloc or vmalloc
@@ -731,7 +731,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
 	/* Setup the descriptor */
 	/* Setup the descriptor */
 	desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc.flags = flags;
 	desc.flags = flags;
-	desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
+	desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */
 	desc.length8 = (u16)(packetlen_aligned >> 3);
 	desc.length8 = (u16)(packetlen_aligned >> 3);
 	desc.transactionid = requestid;
 	desc.transactionid = requestid;
 	desc.rangecount = pagecount;
 	desc.rangecount = pagecount;
@@ -792,7 +792,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
 	/* Setup the descriptor */
 	/* Setup the descriptor */
 	desc->type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc->type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
 	desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
-	desc->dataoffset8 = desc_size >> 3; /* in 8-bytes grandularity */
+	desc->dataoffset8 = desc_size >> 3; /* in 8-bytes granularity */
 	desc->length8 = (u16)(packetlen_aligned >> 3);
 	desc->length8 = (u16)(packetlen_aligned >> 3);
 	desc->transactionid = requestid;
 	desc->transactionid = requestid;
 	desc->rangecount = 1;
 	desc->rangecount = 1;
@@ -842,7 +842,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
 	/* Setup the descriptor */
 	/* Setup the descriptor */
 	desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
 	desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
 	desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
-	desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
+	desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */
 	desc.length8 = (u16)(packetlen_aligned >> 3);
 	desc.length8 = (u16)(packetlen_aligned >> 3);
 	desc.transactionid = requestid;
 	desc.transactionid = requestid;
 	desc.rangecount = 1;
 	desc.rangecount = 1;

+ 24 - 24
drivers/hv/channel_mgmt.c

@@ -1080,30 +1080,30 @@ static void vmbus_onversion_response(
 }
 }
 
 
 /* Channel message dispatch table */
 /* Channel message dispatch table */
-struct vmbus_channel_message_table_entry
-	channel_message_table[CHANNELMSG_COUNT] = {
-	{CHANNELMSG_INVALID,			0, NULL},
-	{CHANNELMSG_OFFERCHANNEL,		0, vmbus_onoffer},
-	{CHANNELMSG_RESCIND_CHANNELOFFER,	0, vmbus_onoffer_rescind},
-	{CHANNELMSG_REQUESTOFFERS,		0, NULL},
-	{CHANNELMSG_ALLOFFERS_DELIVERED,	1, vmbus_onoffers_delivered},
-	{CHANNELMSG_OPENCHANNEL,		0, NULL},
-	{CHANNELMSG_OPENCHANNEL_RESULT,		1, vmbus_onopen_result},
-	{CHANNELMSG_CLOSECHANNEL,		0, NULL},
-	{CHANNELMSG_GPADL_HEADER,		0, NULL},
-	{CHANNELMSG_GPADL_BODY,			0, NULL},
-	{CHANNELMSG_GPADL_CREATED,		1, vmbus_ongpadl_created},
-	{CHANNELMSG_GPADL_TEARDOWN,		0, NULL},
-	{CHANNELMSG_GPADL_TORNDOWN,		1, vmbus_ongpadl_torndown},
-	{CHANNELMSG_RELID_RELEASED,		0, NULL},
-	{CHANNELMSG_INITIATE_CONTACT,		0, NULL},
-	{CHANNELMSG_VERSION_RESPONSE,		1, vmbus_onversion_response},
-	{CHANNELMSG_UNLOAD,			0, NULL},
-	{CHANNELMSG_UNLOAD_RESPONSE,		1, vmbus_unload_response},
-	{CHANNELMSG_18,				0, NULL},
-	{CHANNELMSG_19,				0, NULL},
-	{CHANNELMSG_20,				0, NULL},
-	{CHANNELMSG_TL_CONNECT_REQUEST,		0, NULL},
+const struct vmbus_channel_message_table_entry
+channel_message_table[CHANNELMSG_COUNT] = {
+	{ CHANNELMSG_INVALID,			0, NULL },
+	{ CHANNELMSG_OFFERCHANNEL,		0, vmbus_onoffer },
+	{ CHANNELMSG_RESCIND_CHANNELOFFER,	0, vmbus_onoffer_rescind },
+	{ CHANNELMSG_REQUESTOFFERS,		0, NULL },
+	{ CHANNELMSG_ALLOFFERS_DELIVERED,	1, vmbus_onoffers_delivered },
+	{ CHANNELMSG_OPENCHANNEL,		0, NULL },
+	{ CHANNELMSG_OPENCHANNEL_RESULT,	1, vmbus_onopen_result },
+	{ CHANNELMSG_CLOSECHANNEL,		0, NULL },
+	{ CHANNELMSG_GPADL_HEADER,		0, NULL },
+	{ CHANNELMSG_GPADL_BODY,		0, NULL },
+	{ CHANNELMSG_GPADL_CREATED,		1, vmbus_ongpadl_created },
+	{ CHANNELMSG_GPADL_TEARDOWN,		0, NULL },
+	{ CHANNELMSG_GPADL_TORNDOWN,		1, vmbus_ongpadl_torndown },
+	{ CHANNELMSG_RELID_RELEASED,		0, NULL },
+	{ CHANNELMSG_INITIATE_CONTACT,		0, NULL },
+	{ CHANNELMSG_VERSION_RESPONSE,		1, vmbus_onversion_response },
+	{ CHANNELMSG_UNLOAD,			0, NULL },
+	{ CHANNELMSG_UNLOAD_RESPONSE,		1, vmbus_unload_response },
+	{ CHANNELMSG_18,			0, NULL },
+	{ CHANNELMSG_19,			0, NULL },
+	{ CHANNELMSG_20,			0, NULL },
+	{ CHANNELMSG_TL_CONNECT_REQUEST,	0, NULL },
 };
 };
 
 
 /*
 /*

+ 34 - 31
drivers/hv/connection.c

@@ -296,44 +296,47 @@ struct vmbus_channel *relid2channel(u32 relid)
 
 
 /*
 /*
  * vmbus_on_event - Process a channel event notification
  * vmbus_on_event - Process a channel event notification
+ *
+ * For batched channels (default) optimize host to guest signaling
+ * by ensuring:
+ * 1. While reading the channel, we disable interrupts from host.
+ * 2. Ensure that we process all posted messages from the host
+ *    before returning from this callback.
+ * 3. Once we return, enable signaling from the host. Once this
+ *    state is set we check to see if additional packets are
+ *    available to read. In this case we repeat the process.
+ *    If this tasklet has been running for a long time
+ *    then reschedule ourselves.
  */
  */
 void vmbus_on_event(unsigned long data)
 void vmbus_on_event(unsigned long data)
 {
 {
 	struct vmbus_channel *channel = (void *) data;
 	struct vmbus_channel *channel = (void *) data;
-	void (*callback_fn)(void *);
+	unsigned long time_limit = jiffies + 2;
 
 
-	/*
-	 * A channel once created is persistent even when there
-	 * is no driver handling the device. An unloading driver
-	 * sets the onchannel_callback to NULL on the same CPU
-	 * as where this interrupt is handled (in an interrupt context).
-	 * Thus, checking and invoking the driver specific callback takes
-	 * care of orderly unloading of the driver.
-	 */
-	callback_fn = READ_ONCE(channel->onchannel_callback);
-	if (unlikely(callback_fn == NULL))
-		return;
-
-	(*callback_fn)(channel->channel_callback_context);
-
-	if (channel->callback_mode == HV_CALL_BATCHED) {
-		/*
-		 * This callback reads the messages sent by the host.
-		 * We can optimize host to guest signaling by ensuring:
-		 * 1. While reading the channel, we disable interrupts from
-		 *    host.
-		 * 2. Ensure that we process all posted messages from the host
-		 *    before returning from this callback.
-		 * 3. Once we return, enable signaling from the host. Once this
-		 *    state is set we check to see if additional packets are
-		 *    available to read. In this case we repeat the process.
+	do {
+		void (*callback_fn)(void *);
+
+		/* A channel once created is persistent even when
+		 * there is no driver handling the device. An
+		 * unloading driver sets the onchannel_callback to NULL.
 		 */
 		 */
-		if (hv_end_read(&channel->inbound) != 0) {
-			hv_begin_read(&channel->inbound);
+		callback_fn = READ_ONCE(channel->onchannel_callback);
+		if (unlikely(callback_fn == NULL))
+			return;
 
 
-			tasklet_schedule(&channel->callback_event);
-		}
-	}
+		(*callback_fn)(channel->channel_callback_context);
+
+		if (channel->callback_mode != HV_CALL_BATCHED)
+			return;
+
+		if (likely(hv_end_read(&channel->inbound) == 0))
+			return;
+
+		hv_begin_read(&channel->inbound);
+	} while (likely(time_before(jiffies, time_limit)));
+
+	/* The time limit (2 jiffies) has been reached */
+	tasklet_schedule(&channel->callback_event);
 }
 }
 
 
 /*
 /*

+ 4 - 1
drivers/hv/hv.c

@@ -254,7 +254,10 @@ int hv_synic_init(unsigned int cpu)
 	shared_sint.as_uint64 = 0;
 	shared_sint.as_uint64 = 0;
 	shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
 	shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
 	shared_sint.masked = false;
 	shared_sint.masked = false;
-	shared_sint.auto_eoi = true;
+	if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
+		shared_sint.auto_eoi = false;
+	else
+		shared_sint.auto_eoi = true;
 
 
 	hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
 	hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
 			    shared_sint.as_uint64);
 			    shared_sint.as_uint64);

+ 0 - 2
drivers/hv/hv_balloon.c

@@ -722,8 +722,6 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
 						    5*HZ);
 						    5*HZ);
 		post_status(&dm_device);
 		post_status(&dm_device);
 	}
 	}
-
-	return;
 }
 }
 
 
 static void hv_online_page(struct page *pg)
 static void hv_online_page(struct page *pg)

+ 0 - 2
drivers/hv/hv_fcopy.c

@@ -186,8 +186,6 @@ static void fcopy_send_data(struct work_struct *dummy)
 		}
 		}
 	}
 	}
 	kfree(smsg_out);
 	kfree(smsg_out);
-
-	return;
 }
 }
 
 
 /*
 /*

+ 5 - 7
drivers/hv/hv_kvp.c

@@ -69,7 +69,7 @@ static const int fw_versions[] = {
  *
  *
  * While the request/response protocol is guaranteed by the host, we further
  * While the request/response protocol is guaranteed by the host, we further
  * ensure this by serializing packet processing in this driver - we do not
  * ensure this by serializing packet processing in this driver - we do not
- * read additional packets from the VMBUs until the current packet is fully
+ * read additional packets from the VMBUS until the current packet is fully
  * handled.
  * handled.
  */
  */
 
 
@@ -397,7 +397,7 @@ kvp_send_key(struct work_struct *dummy)
 	 * the max lengths specified. We will however, reserve room
 	 * the max lengths specified. We will however, reserve room
 	 * for the string terminating character - in the utf16s_utf8s()
 	 * for the string terminating character - in the utf16s_utf8s()
 	 * function we limit the size of the buffer where the converted
 	 * function we limit the size of the buffer where the converted
-	 * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee
+	 * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to guarantee
 	 * that the strings can be properly terminated!
 	 * that the strings can be properly terminated!
 	 */
 	 */
 
 
@@ -483,8 +483,6 @@ kvp_send_key(struct work_struct *dummy)
 	}
 	}
 
 
 	kfree(message);
 	kfree(message);
-
-	return;
 }
 }
 
 
 /*
 /*
@@ -533,7 +531,7 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
 	 */
 	 */
 	if (error) {
 	if (error) {
 		/*
 		/*
-		 * Something failed or we have timedout;
+		 * Something failed or we have timed out;
 		 * terminate the current host-side iteration.
 		 * terminate the current host-side iteration.
 		 */
 		 */
 		goto response_done;
 		goto response_done;
@@ -607,8 +605,8 @@ response_done:
  * This callback is invoked when we get a KVP message from the host.
  * This callback is invoked when we get a KVP message from the host.
  * The host ensures that only one KVP transaction can be active at a time.
  * The host ensures that only one KVP transaction can be active at a time.
  * KVP implementation in Linux needs to forward the key to a user-mde
  * KVP implementation in Linux needs to forward the key to a user-mde
- * component to retrive the corresponding value. Consequently, we cannot
- * respond to the host in the conext of this callback. Since the host
+ * component to retrieve the corresponding value. Consequently, we cannot
+ * respond to the host in the context of this callback. Since the host
  * guarantees that at most only one transaction can be active at a time,
  * guarantees that at most only one transaction can be active at a time,
  * we stash away the transaction state in a set of global variables.
  * we stash away the transaction state in a set of global variables.
  */
  */

+ 0 - 2
drivers/hv/hv_snapshot.c

@@ -212,8 +212,6 @@ static void vss_send_op(void)
 	}
 	}
 
 
 	kfree(vss_msg);
 	kfree(vss_msg);
-
-	return;
 }
 }
 
 
 static void vss_handle_request(struct work_struct *dummy)
 static void vss_handle_request(struct work_struct *dummy)

+ 9 - 20
drivers/hv/hyperv_vmbus.h

@@ -218,8 +218,8 @@ struct hv_per_cpu_context {
 
 
 struct hv_context {
 struct hv_context {
 	/* We only support running on top of Hyper-V
 	/* We only support running on top of Hyper-V
-	* So at this point this really can only contain the Hyper-V ID
-	*/
+	 * So at this point this really can only contain the Hyper-V ID
+	 */
 	u64 guestid;
 	u64 guestid;
 
 
 	void *tsc_page;
 	void *tsc_page;
@@ -248,14 +248,6 @@ struct hv_context {
 
 
 extern struct hv_context hv_context;
 extern struct hv_context hv_context;
 
 
-struct hv_ring_buffer_debug_info {
-	u32 current_interrupt_mask;
-	u32 current_read_index;
-	u32 current_write_index;
-	u32 bytes_avail_toread;
-	u32 bytes_avail_towrite;
-};
-
 /* Hv Interface */
 /* Hv Interface */
 
 
 extern int hv_init(void);
 extern int hv_init(void);
@@ -289,9 +281,6 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
 		       void *buffer, u32 buflen, u32 *buffer_actual_len,
 		       void *buffer, u32 buflen, u32 *buffer_actual_len,
 		       u64 *requestid, bool raw);
 		       u64 *requestid, bool raw);
 
 
-void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
-				 struct hv_ring_buffer_debug_info *debug_info);
-
 /*
 /*
  * Maximum channels is determined by the size of the interrupt page
  * Maximum channels is determined by the size of the interrupt page
  * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt
  * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt
@@ -376,7 +365,7 @@ struct vmbus_channel_message_table_entry {
 	void (*message_handler)(struct vmbus_channel_message_header *msg);
 	void (*message_handler)(struct vmbus_channel_message_header *msg);
 };
 };
 
 
-extern struct vmbus_channel_message_table_entry
+extern const struct vmbus_channel_message_table_entry
 	channel_message_table[CHANNELMSG_COUNT];
 	channel_message_table[CHANNELMSG_COUNT];
 
 
 
 
@@ -403,17 +392,17 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep);
 void vmbus_on_event(unsigned long data);
 void vmbus_on_event(unsigned long data);
 void vmbus_on_msg_dpc(unsigned long data);
 void vmbus_on_msg_dpc(unsigned long data);
 
 
-int hv_kvp_init(struct hv_util_service *);
+int hv_kvp_init(struct hv_util_service *srv);
 void hv_kvp_deinit(void);
 void hv_kvp_deinit(void);
-void hv_kvp_onchannelcallback(void *);
+void hv_kvp_onchannelcallback(void *context);
 
 
-int hv_vss_init(struct hv_util_service *);
+int hv_vss_init(struct hv_util_service *srv);
 void hv_vss_deinit(void);
 void hv_vss_deinit(void);
-void hv_vss_onchannelcallback(void *);
+void hv_vss_onchannelcallback(void *context);
 
 
-int hv_fcopy_init(struct hv_util_service *);
+int hv_fcopy_init(struct hv_util_service *srv);
 void hv_fcopy_deinit(void);
 void hv_fcopy_deinit(void);
-void hv_fcopy_onchannelcallback(void *);
+void hv_fcopy_onchannelcallback(void *context);
 void vmbus_initiate_unload(bool crash);
 void vmbus_initiate_unload(bool crash);
 
 
 static inline void hv_poll_channel(struct vmbus_channel *channel,
 static inline void hv_poll_channel(struct vmbus_channel *channel,

+ 8 - 14
drivers/hv/ring_buffer.c

@@ -75,8 +75,6 @@ static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel)
 	 */
 	 */
 	if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
 	if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
 		vmbus_setevent(channel);
 		vmbus_setevent(channel);
-
-	return;
 }
 }
 
 
 /* Get the next write location for the specified ring buffer. */
 /* Get the next write location for the specified ring buffer. */
@@ -210,6 +208,7 @@ void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
 			ring_info->ring_buffer->interrupt_mask;
 			ring_info->ring_buffer->interrupt_mask;
 	}
 	}
 }
 }
+EXPORT_SYMBOL_GPL(hv_ringbuffer_get_debuginfo);
 
 
 /* Initialize the ring buffer. */
 /* Initialize the ring buffer. */
 int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
 int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
@@ -269,14 +268,13 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
 int hv_ringbuffer_write(struct vmbus_channel *channel,
 int hv_ringbuffer_write(struct vmbus_channel *channel,
 			const struct kvec *kv_list, u32 kv_count)
 			const struct kvec *kv_list, u32 kv_count)
 {
 {
-	int i = 0;
+	int i;
 	u32 bytes_avail_towrite;
 	u32 bytes_avail_towrite;
-	u32 totalbytes_towrite = 0;
-
+	u32 totalbytes_towrite = sizeof(u64);
 	u32 next_write_location;
 	u32 next_write_location;
 	u32 old_write;
 	u32 old_write;
-	u64 prev_indices = 0;
-	unsigned long flags = 0;
+	u64 prev_indices;
+	unsigned long flags;
 	struct hv_ring_buffer_info *outring_info = &channel->outbound;
 	struct hv_ring_buffer_info *outring_info = &channel->outbound;
 
 
 	if (channel->rescind)
 	if (channel->rescind)
@@ -285,8 +283,6 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
 	for (i = 0; i < kv_count; i++)
 	for (i = 0; i < kv_count; i++)
 		totalbytes_towrite += kv_list[i].iov_len;
 		totalbytes_towrite += kv_list[i].iov_len;
 
 
-	totalbytes_towrite += sizeof(u64);
-
 	spin_lock_irqsave(&outring_info->ring_lock, flags);
 	spin_lock_irqsave(&outring_info->ring_lock, flags);
 
 
 	bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
 	bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
@@ -349,18 +345,16 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
 		       u64 *requestid, bool raw)
 		       u64 *requestid, bool raw)
 {
 {
 	u32 bytes_avail_toread;
 	u32 bytes_avail_toread;
-	u32 next_read_location = 0;
+	u32 next_read_location;
 	u64 prev_indices = 0;
 	u64 prev_indices = 0;
 	struct vmpacket_descriptor desc;
 	struct vmpacket_descriptor desc;
 	u32 offset;
 	u32 offset;
 	u32 packetlen;
 	u32 packetlen;
-	int ret = 0;
 	struct hv_ring_buffer_info *inring_info = &channel->inbound;
 	struct hv_ring_buffer_info *inring_info = &channel->inbound;
 
 
 	if (buflen <= 0)
 	if (buflen <= 0)
 		return -EINVAL;
 		return -EINVAL;
 
 
-
 	*buffer_actual_len = 0;
 	*buffer_actual_len = 0;
 	*requestid = 0;
 	*requestid = 0;
 
 
@@ -371,7 +365,7 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
 		 * No error is set when there is even no header, drivers are
 		 * No error is set when there is even no header, drivers are
 		 * supposed to analyze buffer_actual_len.
 		 * supposed to analyze buffer_actual_len.
 		 */
 		 */
-		return ret;
+		return 0;
 	}
 	}
 
 
 	init_cached_read_index(inring_info);
 	init_cached_read_index(inring_info);
@@ -417,7 +411,7 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
 
 
 	hv_signal_on_read(channel);
 	hv_signal_on_read(channel);
 
 
-	return ret;
+	return 0;
 }
 }
 
 
 /*
 /*

+ 1 - 3
drivers/hv/vmbus_drv.c

@@ -787,8 +787,6 @@ static void vmbus_shutdown(struct device *child_device)
 
 
 	if (drv->shutdown)
 	if (drv->shutdown)
 		drv->shutdown(dev);
 		drv->shutdown(dev);
-
-	return;
 }
 }
 
 
 
 
@@ -855,7 +853,7 @@ void vmbus_on_msg_dpc(unsigned long data)
 	struct hv_message *msg = (struct hv_message *)page_addr +
 	struct hv_message *msg = (struct hv_message *)page_addr +
 				  VMBUS_MESSAGE_SINT;
 				  VMBUS_MESSAGE_SINT;
 	struct vmbus_channel_message_header *hdr;
 	struct vmbus_channel_message_header *hdr;
-	struct vmbus_channel_message_table_entry *entry;
+	const struct vmbus_channel_message_table_entry *entry;
 	struct onmessage_work_context *ctx;
 	struct onmessage_work_context *ctx;
 	u32 message_type = msg->header.message_type;
 	u32 message_type = msg->header.message_type;
 
 

+ 1 - 1
drivers/hwtracing/coresight/of_coresight.c

@@ -149,7 +149,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
 				continue;
 				continue;
 
 
 			/* The local out port number */
 			/* The local out port number */
-			pdata->outports[i] = endpoint.id;
+			pdata->outports[i] = endpoint.port;
 
 
 			/*
 			/*
 			 * Get a handle on the remote port and parent
 			 * Get a handle on the remote port and parent

+ 4 - 11
drivers/iio/industrialio-core.c

@@ -1719,18 +1719,13 @@ int iio_device_register(struct iio_dev *indio_dev)
 
 
 	cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
 	cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
 	indio_dev->chrdev.owner = indio_dev->info->driver_module;
 	indio_dev->chrdev.owner = indio_dev->info->driver_module;
-	indio_dev->chrdev.kobj.parent = &indio_dev->dev.kobj;
-	ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
-	if (ret < 0)
-		goto error_unreg_eventset;
 
 
-	ret = device_add(&indio_dev->dev);
+	ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
 	if (ret < 0)
 	if (ret < 0)
-		goto error_cdev_del;
+		goto error_unreg_eventset;
 
 
 	return 0;
 	return 0;
-error_cdev_del:
-	cdev_del(&indio_dev->chrdev);
+
 error_unreg_eventset:
 error_unreg_eventset:
 	iio_device_unregister_eventset(indio_dev);
 	iio_device_unregister_eventset(indio_dev);
 error_free_sysfs:
 error_free_sysfs:
@@ -1751,10 +1746,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
 {
 {
 	mutex_lock(&indio_dev->info_exist_lock);
 	mutex_lock(&indio_dev->info_exist_lock);
 
 
-	device_del(&indio_dev->dev);
+	cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
 
 
-	if (indio_dev->chrdev.dev)
-		cdev_del(&indio_dev->chrdev);
 	iio_device_unregister_debugfs(indio_dev);
 	iio_device_unregister_debugfs(indio_dev);
 
 
 	iio_disable_all_buffers(indio_dev);
 	iio_disable_all_buffers(indio_dev);

+ 18 - 17
drivers/infiniband/core/ucm.c

@@ -1205,12 +1205,15 @@ static void ib_ucm_release_dev(struct device *dev)
 	struct ib_ucm_device *ucm_dev;
 	struct ib_ucm_device *ucm_dev;
 
 
 	ucm_dev = container_of(dev, struct ib_ucm_device, dev);
 	ucm_dev = container_of(dev, struct ib_ucm_device, dev);
-	cdev_del(&ucm_dev->cdev);
+	kfree(ucm_dev);
+}
+
+static void ib_ucm_free_dev(struct ib_ucm_device *ucm_dev)
+{
 	if (ucm_dev->devnum < IB_UCM_MAX_DEVICES)
 	if (ucm_dev->devnum < IB_UCM_MAX_DEVICES)
 		clear_bit(ucm_dev->devnum, dev_map);
 		clear_bit(ucm_dev->devnum, dev_map);
 	else
 	else
 		clear_bit(ucm_dev->devnum - IB_UCM_MAX_DEVICES, overflow_map);
 		clear_bit(ucm_dev->devnum - IB_UCM_MAX_DEVICES, overflow_map);
-	kfree(ucm_dev);
 }
 }
 
 
 static const struct file_operations ucm_fops = {
 static const struct file_operations ucm_fops = {
@@ -1266,7 +1269,9 @@ static void ib_ucm_add_one(struct ib_device *device)
 	if (!ucm_dev)
 	if (!ucm_dev)
 		return;
 		return;
 
 
+	device_initialize(&ucm_dev->dev);
 	ucm_dev->ib_dev = device;
 	ucm_dev->ib_dev = device;
+	ucm_dev->dev.release = ib_ucm_release_dev;
 
 
 	devnum = find_first_zero_bit(dev_map, IB_UCM_MAX_DEVICES);
 	devnum = find_first_zero_bit(dev_map, IB_UCM_MAX_DEVICES);
 	if (devnum >= IB_UCM_MAX_DEVICES) {
 	if (devnum >= IB_UCM_MAX_DEVICES) {
@@ -1286,16 +1291,14 @@ static void ib_ucm_add_one(struct ib_device *device)
 	cdev_init(&ucm_dev->cdev, &ucm_fops);
 	cdev_init(&ucm_dev->cdev, &ucm_fops);
 	ucm_dev->cdev.owner = THIS_MODULE;
 	ucm_dev->cdev.owner = THIS_MODULE;
 	kobject_set_name(&ucm_dev->cdev.kobj, "ucm%d", ucm_dev->devnum);
 	kobject_set_name(&ucm_dev->cdev.kobj, "ucm%d", ucm_dev->devnum);
-	if (cdev_add(&ucm_dev->cdev, base, 1))
-		goto err;
 
 
 	ucm_dev->dev.class = &cm_class;
 	ucm_dev->dev.class = &cm_class;
 	ucm_dev->dev.parent = device->dev.parent;
 	ucm_dev->dev.parent = device->dev.parent;
-	ucm_dev->dev.devt = ucm_dev->cdev.dev;
-	ucm_dev->dev.release = ib_ucm_release_dev;
+	ucm_dev->dev.devt = base;
+
 	dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum);
 	dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum);
-	if (device_register(&ucm_dev->dev))
-		goto err_cdev;
+	if (cdev_device_add(&ucm_dev->cdev, &ucm_dev->dev))
+		goto err_devnum;
 
 
 	if (device_create_file(&ucm_dev->dev, &dev_attr_ibdev))
 	if (device_create_file(&ucm_dev->dev, &dev_attr_ibdev))
 		goto err_dev;
 		goto err_dev;
@@ -1304,15 +1307,11 @@ static void ib_ucm_add_one(struct ib_device *device)
 	return;
 	return;
 
 
 err_dev:
 err_dev:
-	device_unregister(&ucm_dev->dev);
-err_cdev:
-	cdev_del(&ucm_dev->cdev);
-	if (ucm_dev->devnum < IB_UCM_MAX_DEVICES)
-		clear_bit(devnum, dev_map);
-	else
-		clear_bit(devnum, overflow_map);
+	cdev_device_del(&ucm_dev->cdev, &ucm_dev->dev);
+err_devnum:
+	ib_ucm_free_dev(ucm_dev);
 err:
 err:
-	kfree(ucm_dev);
+	put_device(&ucm_dev->dev);
 	return;
 	return;
 }
 }
 
 
@@ -1323,7 +1322,9 @@ static void ib_ucm_remove_one(struct ib_device *device, void *client_data)
 	if (!ucm_dev)
 	if (!ucm_dev)
 		return;
 		return;
 
 
-	device_unregister(&ucm_dev->dev);
+	cdev_device_del(&ucm_dev->cdev, &ucm_dev->dev);
+	ib_ucm_free_dev(ucm_dev);
+	put_device(&ucm_dev->dev);
 }
 }
 
 
 static CLASS_ATTR_STRING(abi_version, S_IRUGO,
 static CLASS_ATTR_STRING(abi_version, S_IRUGO,

+ 2 - 2
drivers/infiniband/core/user_mad.c

@@ -1187,7 +1187,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
 
 
 	cdev_init(&port->cdev, &umad_fops);
 	cdev_init(&port->cdev, &umad_fops);
 	port->cdev.owner = THIS_MODULE;
 	port->cdev.owner = THIS_MODULE;
-	port->cdev.kobj.parent = &umad_dev->kobj;
+	cdev_set_parent(&port->cdev, &umad_dev->kobj);
 	kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
 	kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
 	if (cdev_add(&port->cdev, base, 1))
 	if (cdev_add(&port->cdev, base, 1))
 		goto err_cdev;
 		goto err_cdev;
@@ -1206,7 +1206,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
 	base += IB_UMAD_MAX_PORTS;
 	base += IB_UMAD_MAX_PORTS;
 	cdev_init(&port->sm_cdev, &umad_sm_fops);
 	cdev_init(&port->sm_cdev, &umad_sm_fops);
 	port->sm_cdev.owner = THIS_MODULE;
 	port->sm_cdev.owner = THIS_MODULE;
-	port->sm_cdev.kobj.parent = &umad_dev->kobj;
+	cdev_set_parent(&port->sm_cdev, &umad_dev->kobj);
 	kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
 	kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
 	if (cdev_add(&port->sm_cdev, base, 1))
 	if (cdev_add(&port->sm_cdev, base, 1))
 		goto err_sm_cdev;
 		goto err_sm_cdev;

+ 1 - 1
drivers/infiniband/core/uverbs_main.c

@@ -1093,7 +1093,7 @@ static void ib_uverbs_add_one(struct ib_device *device)
 	cdev_init(&uverbs_dev->cdev, NULL);
 	cdev_init(&uverbs_dev->cdev, NULL);
 	uverbs_dev->cdev.owner = THIS_MODULE;
 	uverbs_dev->cdev.owner = THIS_MODULE;
 	uverbs_dev->cdev.ops = device->mmap ? &uverbs_mmap_fops : &uverbs_fops;
 	uverbs_dev->cdev.ops = device->mmap ? &uverbs_mmap_fops : &uverbs_fops;
-	uverbs_dev->cdev.kobj.parent = &uverbs_dev->kobj;
+	cdev_set_parent(&uverbs_dev->cdev, &uverbs_dev->kobj);
 	kobject_set_name(&uverbs_dev->cdev.kobj, "uverbs%d", uverbs_dev->devnum);
 	kobject_set_name(&uverbs_dev->cdev.kobj, "uverbs%d", uverbs_dev->devnum);
 	if (cdev_add(&uverbs_dev->cdev, base, 1))
 	if (cdev_add(&uverbs_dev->cdev, base, 1))
 		goto err_cdev;
 		goto err_cdev;

+ 1 - 1
drivers/infiniband/hw/hfi1/device.c

@@ -69,7 +69,7 @@ int hfi1_cdev_init(int minor, const char *name,
 
 
 	cdev_init(cdev, fops);
 	cdev_init(cdev, fops);
 	cdev->owner = THIS_MODULE;
 	cdev->owner = THIS_MODULE;
-	cdev->kobj.parent = parent;
+	cdev_set_parent(cdev, parent);
 	kobject_set_name(&cdev->kobj, name);
 	kobject_set_name(&cdev->kobj, name);
 
 
 	ret = cdev_add(cdev, dev, 1);
 	ret = cdev_add(cdev, dev, 1);

+ 2 - 9
drivers/input/evdev.c

@@ -1354,8 +1354,6 @@ static void evdev_cleanup(struct evdev *evdev)
 	evdev_mark_dead(evdev);
 	evdev_mark_dead(evdev);
 	evdev_hangup(evdev);
 	evdev_hangup(evdev);
 
 
-	cdev_del(&evdev->cdev);
-
 	/* evdev is marked dead so no one else accesses evdev->open */
 	/* evdev is marked dead so no one else accesses evdev->open */
 	if (evdev->open) {
 	if (evdev->open) {
 		input_flush_device(handle, NULL);
 		input_flush_device(handle, NULL);
@@ -1416,12 +1414,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 		goto err_free_evdev;
 		goto err_free_evdev;
 
 
 	cdev_init(&evdev->cdev, &evdev_fops);
 	cdev_init(&evdev->cdev, &evdev_fops);
-	evdev->cdev.kobj.parent = &evdev->dev.kobj;
-	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
-	if (error)
-		goto err_unregister_handle;
 
 
-	error = device_add(&evdev->dev);
+	error = cdev_device_add(&evdev->cdev, &evdev->dev);
 	if (error)
 	if (error)
 		goto err_cleanup_evdev;
 		goto err_cleanup_evdev;
 
 
@@ -1429,7 +1423,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 
 
  err_cleanup_evdev:
  err_cleanup_evdev:
 	evdev_cleanup(evdev);
 	evdev_cleanup(evdev);
- err_unregister_handle:
 	input_unregister_handle(&evdev->handle);
 	input_unregister_handle(&evdev->handle);
  err_free_evdev:
  err_free_evdev:
 	put_device(&evdev->dev);
 	put_device(&evdev->dev);
@@ -1442,7 +1435,7 @@ static void evdev_disconnect(struct input_handle *handle)
 {
 {
 	struct evdev *evdev = handle->private;
 	struct evdev *evdev = handle->private;
 
 
-	device_del(&evdev->dev);
+	cdev_device_del(&evdev->cdev, &evdev->dev);
 	evdev_cleanup(evdev);
 	evdev_cleanup(evdev);
 	input_free_minor(MINOR(evdev->dev.devt));
 	input_free_minor(MINOR(evdev->dev.devt));
 	input_unregister_handle(handle);
 	input_unregister_handle(handle);

+ 2 - 9
drivers/input/joydev.c

@@ -742,8 +742,6 @@ static void joydev_cleanup(struct joydev *joydev)
 	joydev_mark_dead(joydev);
 	joydev_mark_dead(joydev);
 	joydev_hangup(joydev);
 	joydev_hangup(joydev);
 
 
-	cdev_del(&joydev->cdev);
-
 	/* joydev is marked dead so no one else accesses joydev->open */
 	/* joydev is marked dead so no one else accesses joydev->open */
 	if (joydev->open)
 	if (joydev->open)
 		input_close_device(handle);
 		input_close_device(handle);
@@ -913,12 +911,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
 		goto err_free_joydev;
 		goto err_free_joydev;
 
 
 	cdev_init(&joydev->cdev, &joydev_fops);
 	cdev_init(&joydev->cdev, &joydev_fops);
-	joydev->cdev.kobj.parent = &joydev->dev.kobj;
-	error = cdev_add(&joydev->cdev, joydev->dev.devt, 1);
-	if (error)
-		goto err_unregister_handle;
 
 
-	error = device_add(&joydev->dev);
+	error = cdev_device_add(&joydev->cdev, &joydev->dev);
 	if (error)
 	if (error)
 		goto err_cleanup_joydev;
 		goto err_cleanup_joydev;
 
 
@@ -926,7 +920,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
 
 
  err_cleanup_joydev:
  err_cleanup_joydev:
 	joydev_cleanup(joydev);
 	joydev_cleanup(joydev);
- err_unregister_handle:
 	input_unregister_handle(&joydev->handle);
 	input_unregister_handle(&joydev->handle);
  err_free_joydev:
  err_free_joydev:
 	put_device(&joydev->dev);
 	put_device(&joydev->dev);
@@ -939,7 +932,7 @@ static void joydev_disconnect(struct input_handle *handle)
 {
 {
 	struct joydev *joydev = handle->private;
 	struct joydev *joydev = handle->private;
 
 
-	device_del(&joydev->dev);
+	cdev_device_del(&joydev->cdev, &joydev->dev);
 	joydev_cleanup(joydev);
 	joydev_cleanup(joydev);
 	input_free_minor(MINOR(joydev->dev.devt));
 	input_free_minor(MINOR(joydev->dev.devt));
 	input_unregister_handle(handle);
 	input_unregister_handle(handle);

+ 2 - 9
drivers/input/mousedev.c

@@ -812,8 +812,6 @@ static void mousedev_cleanup(struct mousedev *mousedev)
 	mousedev_mark_dead(mousedev);
 	mousedev_mark_dead(mousedev);
 	mousedev_hangup(mousedev);
 	mousedev_hangup(mousedev);
 
 
-	cdev_del(&mousedev->cdev);
-
 	/* mousedev is marked dead so no one else accesses mousedev->open */
 	/* mousedev is marked dead so no one else accesses mousedev->open */
 	if (mousedev->open)
 	if (mousedev->open)
 		input_close_device(handle);
 		input_close_device(handle);
@@ -901,12 +899,8 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
 	}
 	}
 
 
 	cdev_init(&mousedev->cdev, &mousedev_fops);
 	cdev_init(&mousedev->cdev, &mousedev_fops);
-	mousedev->cdev.kobj.parent = &mousedev->dev.kobj;
-	error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
-	if (error)
-		goto err_unregister_handle;
 
 
-	error = device_add(&mousedev->dev);
+	error = cdev_device_add(&mousedev->cdev, &mousedev->dev);
 	if (error)
 	if (error)
 		goto err_cleanup_mousedev;
 		goto err_cleanup_mousedev;
 
 
@@ -914,7 +908,6 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
 
 
  err_cleanup_mousedev:
  err_cleanup_mousedev:
 	mousedev_cleanup(mousedev);
 	mousedev_cleanup(mousedev);
- err_unregister_handle:
 	if (!mixdev)
 	if (!mixdev)
 		input_unregister_handle(&mousedev->handle);
 		input_unregister_handle(&mousedev->handle);
  err_free_mousedev:
  err_free_mousedev:
@@ -927,7 +920,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
 
 
 static void mousedev_destroy(struct mousedev *mousedev)
 static void mousedev_destroy(struct mousedev *mousedev)
 {
 {
-	device_del(&mousedev->dev);
+	cdev_device_del(&mousedev->cdev, &mousedev->dev);
 	mousedev_cleanup(mousedev);
 	mousedev_cleanup(mousedev);
 	input_free_minor(MINOR(mousedev->dev.devt));
 	input_free_minor(MINOR(mousedev->dev.devt));
 	if (mousedev != mousedev_mix)
 	if (mousedev != mousedev_mix)

+ 4 - 12
drivers/media/cec/cec-core.c

@@ -137,24 +137,17 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
 
 
 	/* Part 2: Initialize and register the character device */
 	/* Part 2: Initialize and register the character device */
 	cdev_init(&devnode->cdev, &cec_devnode_fops);
 	cdev_init(&devnode->cdev, &cec_devnode_fops);
-	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 	devnode->cdev.owner = owner;
 	devnode->cdev.owner = owner;
 
 
-	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
-	if (ret < 0) {
-		pr_err("%s: cdev_add failed\n", __func__);
+	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+	if (ret) {
+		pr_err("%s: cdev_device_add failed\n", __func__);
 		goto clr_bit;
 		goto clr_bit;
 	}
 	}
 
 
-	ret = device_add(&devnode->dev);
-	if (ret)
-		goto cdev_del;
-
 	devnode->registered = true;
 	devnode->registered = true;
 	return 0;
 	return 0;
 
 
-cdev_del:
-	cdev_del(&devnode->cdev);
 clr_bit:
 clr_bit:
 	mutex_lock(&cec_devnode_lock);
 	mutex_lock(&cec_devnode_lock);
 	clear_bit(devnode->minor, cec_devnode_nums);
 	clear_bit(devnode->minor, cec_devnode_nums);
@@ -190,8 +183,7 @@ static void cec_devnode_unregister(struct cec_devnode *devnode)
 	devnode->unregistered = true;
 	devnode->unregistered = true;
 	mutex_unlock(&devnode->lock);
 	mutex_unlock(&devnode->lock);
 
 
-	device_del(&devnode->dev);
-	cdev_del(&devnode->cdev);
+	cdev_device_del(&devnode->cdev, &devnode->dev);
 	put_device(&devnode->dev);
 	put_device(&devnode->dev);
 }
 }
 
 

+ 5 - 15
drivers/media/media-devnode.c

@@ -248,31 +248,22 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
 	device_initialize(&devnode->dev);
 	device_initialize(&devnode->dev);
 
 
-	/* Part 2: Initialize and register the character device */
+	/* Part 2: Initialize the character device */
 	cdev_init(&devnode->cdev, &media_devnode_fops);
 	cdev_init(&devnode->cdev, &media_devnode_fops);
 	devnode->cdev.owner = owner;
 	devnode->cdev.owner = owner;
-	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 
 
-	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
+	/* Part 3: Add the media and char device */
+	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
 	if (ret < 0) {
 	if (ret < 0) {
-		pr_err("%s: cdev_add failed\n", __func__);
+		pr_err("%s: cdev_device_add failed\n", __func__);
 		goto cdev_add_error;
 		goto cdev_add_error;
 	}
 	}
 
 
-	/* Part 3: Add the media device */
-	ret = device_add(&devnode->dev);
-	if (ret < 0) {
-		pr_err("%s: device_add failed\n", __func__);
-		goto device_add_error;
-	}
-
 	/* Part 4: Activate this minor. The char device can now be used. */
 	/* Part 4: Activate this minor. The char device can now be used. */
 	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 
 
 	return 0;
 	return 0;
 
 
-device_add_error:
-	cdev_del(&devnode->cdev);
 cdev_add_error:
 cdev_add_error:
 	mutex_lock(&media_devnode_lock);
 	mutex_lock(&media_devnode_lock);
 	clear_bit(devnode->minor, media_devnode_nums);
 	clear_bit(devnode->minor, media_devnode_nums);
@@ -298,9 +289,8 @@ void media_devnode_unregister(struct media_devnode *devnode)
 {
 {
 	mutex_lock(&media_devnode_lock);
 	mutex_lock(&media_devnode_lock);
 	/* Delete the cdev on this minor as well */
 	/* Delete the cdev on this minor as well */
-	cdev_del(&devnode->cdev);
+	cdev_device_del(&devnode->cdev, &devnode->dev);
 	mutex_unlock(&media_devnode_lock);
 	mutex_unlock(&media_devnode_lock);
-	device_del(&devnode->dev);
 	devnode->media_dev = NULL;
 	devnode->media_dev = NULL;
 	put_device(&devnode->dev);
 	put_device(&devnode->dev);
 }
 }

+ 6 - 287
drivers/misc/Kconfig

@@ -419,16 +419,6 @@ config VMWARE_BALLOON
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called vmw_balloon.
 	  module will be called vmw_balloon.
 
 
-config ARM_CHARLCD
-	bool "ARM Ltd. Character LCD Driver"
-	depends on PLAT_VERSATILE
-	help
-	  This is a driver for the character LCD found on the ARM Ltd.
-	  Versatile and RealView Platform Baseboards. It doesn't do
-	  very much more than display the text "ARM Linux" on the first
-	  line and the Linux version on the second line, but that's
-	  still useful.
-
 config PCH_PHUB
 config PCH_PHUB
 	tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
 	tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
 	select GENERIC_NET_UTILS
 	select GENERIC_NET_UTILS
@@ -492,284 +482,13 @@ config VEXPRESS_SYSCFG
 	  bus. System Configuration interface is one of the possible means
 	  bus. System Configuration interface is one of the possible means
 	  of generating transactions on this bus.
 	  of generating transactions on this bus.
 
 
-config PANEL
-	tristate "Parallel port LCD/Keypad Panel support"
-	depends on PARPORT
-	---help---
-	  Say Y here if you have an HD44780 or KS-0074 LCD connected to your
-	  parallel port. This driver also features 4 and 6-key keypads. The LCD
-	  is accessible through the /dev/lcd char device (10, 156), and the
-	  keypad through /dev/keypad (10, 185). This code can either be
-	  compiled as a module, or linked into the kernel and started at boot.
-	  If you don't understand what all this is about, say N.
-
-if PANEL
-
-config PANEL_PARPORT
-	int "Default parallel port number (0=LPT1)"
-	range 0 255
-	default "0"
-	---help---
-	  This is the index of the parallel port the panel is connected to. One
-	  driver instance only supports one parallel port, so if your keypad
-	  and LCD are connected to two separate ports, you have to start two
-	  modules with different arguments. Numbering starts with '0' for LPT1,
-	  and so on.
-
-config PANEL_PROFILE
-	int "Default panel profile (0-5, 0=custom)"
-	range 0 5
-	default "5"
-	---help---
-	  To ease configuration, the driver supports different configuration
-	  profiles for past and recent wirings. These profiles can also be
-	  used to define an approximative configuration, completed by a few
-	  other options. Here are the profiles :
-
-	    0 = custom (see further)
-	    1 = 2x16 parallel LCD, old keypad
-	    2 = 2x16 serial LCD (KS-0074), new keypad
-	    3 = 2x16 parallel LCD (Hantronix), no keypad
-	    4 = 2x16 parallel LCD (Nexcom NSA1045) with Nexcom's keypad
-	    5 = 2x40 parallel LCD (old one), with old keypad
-
-	  Custom configurations allow you to define how your display is
-	  wired to the parallel port, and how it works. This is only intended
-	  for experts.
-
-config PANEL_KEYPAD
-	depends on PANEL_PROFILE="0"
-	int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)"
-	range 0 3
-	default 0
-	---help---
-	  This enables and configures a keypad connected to the parallel port.
-	  The keys will be read from character device 10,185. Valid values are :
-
-	    0 : do not enable this driver
-	    1 : old 6 keys keypad
-	    2 : new 6 keys keypad, as used on the server at www.ant-computing.com
-	    3 : Nexcom NSA1045's 4 keys keypad
-
-	  New profiles can be described in the driver source. The driver also
-	  supports simultaneous keys pressed when the keypad supports them.
-
-config PANEL_LCD
-	depends on PANEL_PROFILE="0"
-	int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)"
-	range 0 5
-	default 0
-	---help---
-	   This enables and configures an LCD connected to the parallel port.
-	   The driver includes an interpreter for escape codes starting with
-	   '\e[L' which are specific to the LCD, and a few ANSI codes. The
-	   driver will be registered as character device 10,156, usually
-	   under the name '/dev/lcd'. There are a total of 6 supported types :
-
-	     0 : do not enable the driver
-	     1 : custom configuration and wiring (see further)
-	     2 : 2x16 & 2x40 parallel LCD (old wiring)
-	     3 : 2x16 serial LCD (KS-0074 based)
-	     4 : 2x16 parallel LCD (Hantronix wiring)
-	     5 : 2x16 parallel LCD (Nexcom wiring)
-
-	   When type '1' is specified, other options will appear to configure
-	   more precise aspects (wiring, dimensions, protocol, ...). Please note
-	   that those values changed from the 2.4 driver for better consistency.
-
-config PANEL_LCD_HEIGHT
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "Number of lines on the LCD (1-2)"
-	range 1 2
-	default 2
-	---help---
-	  This is the number of visible character lines on the LCD in custom profile.
-	  It can either be 1 or 2.
-
-config PANEL_LCD_WIDTH
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "Number of characters per line on the LCD (1-40)"
-	range 1 40
-	default 40
-	---help---
-	  This is the number of characters per line on the LCD in custom profile.
-	  Common values are 16,20,24,40.
-
-config PANEL_LCD_BWIDTH
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "Internal LCD line width (1-40, 40 by default)"
-	range 1 40
-	default 40
-	---help---
-	  Most LCDs use a standard controller which supports hardware lines of 40
-	  characters, although sometimes only 16, 20 or 24 of them are really wired
-	  to the terminal. This results in some non-visible but addressable characters,
-	  and is the case for most parallel LCDs. Other LCDs, and some serial ones,
-	  however, use the same line width internally as what is visible. The KS0074
-	  for example, uses 16 characters per line for 16 visible characters per line.
-
-	  This option lets you configure the value used by your LCD in 'custom' profile.
-	  If you don't know, put '40' here.
-
-config PANEL_LCD_HWIDTH
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "Hardware LCD line width (1-64, 64 by default)"
-	range 1 64
-	default 64
-	---help---
-	  Most LCDs use a single address bit to differentiate line 0 and line 1. Since
-	  some of them need to be able to address 40 chars with the lower bits, they
-	  often use the immediately superior power of 2, which is 64, to address the
-	  next line.
-
-	  If you don't know what your LCD uses, in doubt let 16 here for a 2x16, and
-	  64 here for a 2x40.
-
-config PANEL_LCD_CHARSET
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "LCD character set (0=normal, 1=KS0074)"
-	range 0 1
-	default 0
-	---help---
-	  Some controllers such as the KS0074 use a somewhat strange character set
-	  where many symbols are at unusual places. The driver knows how to map
-	  'standard' ASCII characters to the character sets used by these controllers.
-	  Valid values are :
-
-	     0 : normal (untranslated) character set
-	     1 : KS0074 character set
-
-	  If you don't know, use the normal one (0).
-
-config PANEL_LCD_PROTO
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-	int "LCD communication mode (0=parallel 8 bits, 1=serial)"
-	range 0 1
-	default 0
-	---help---
-	  This driver now supports any serial or parallel LCD wired to a parallel
-	  port. But before assigning signals, the driver needs to know if it will
-	  be driving a serial LCD or a parallel one. Serial LCDs only use 2 wires
-	  (SDA/SCL), while parallel ones use 2 or 3 wires for the control signals
-	  (E, RS, sometimes RW), and 4 or 8 for the data. Use 0 here for a 8 bits
-	  parallel LCD, and 1 for a serial LCD.
-
-config PANEL_LCD_PIN_E
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
-        int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) "
-	range -17 17
-	default 14
+config ASPEED_LPC_CTRL
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
 	---help---
 	---help---
-	  This describes the number of the parallel port pin to which the LCD 'E'
-	  signal has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'E' pin in custom profile is '14' (AUTOFEED).
-
-config PANEL_LCD_PIN_RS
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
-        int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) "
-	range -17 17
-	default 17
-	---help---
-	  This describes the number of the parallel port pin to which the LCD 'RS'
-	  signal has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'RS' pin in custom profile is '17' (SELECT IN).
-
-config PANEL_LCD_PIN_RW
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
-        int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) "
-	range -17 17
-	default 16
-	---help---
-	  This describes the number of the parallel port pin to which the LCD 'RW'
-	  signal has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'RW' pin in custom profile is '16' (INIT).
-
-config PANEL_LCD_PIN_SCL
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
-        int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) "
-	range -17 17
-	default 1
-	---help---
-	  This describes the number of the parallel port pin to which the serial
-	  LCD 'SCL' signal has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'SCL' pin in custom profile is '1' (STROBE).
-
-config PANEL_LCD_PIN_SDA
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
-        int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) "
-	range -17 17
-	default 2
-	---help---
-	  This describes the number of the parallel port pin to which the serial
-	  LCD 'SDA' signal has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'SDA' pin in custom profile is '2' (D0).
-
-config PANEL_LCD_PIN_BL
-	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
-        int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) "
-	range -17 17
-	default 0
-	---help---
-	  This describes the number of the parallel port pin to which the LCD 'BL' signal
-          has been connected. It can be :
-
-	          0 : no connection (eg: connected to ground)
-	      1..17 : directly connected to any of these pins on the DB25 plug
-	    -1..-17 : connected to the same pin through an inverter (eg: transistor).
-
-	  Default for the 'BL' pin in custom profile is '0' (uncontrolled).
-
-config PANEL_CHANGE_MESSAGE
-	bool "Change LCD initialization message ?"
-	default "n"
-	---help---
-	  This allows you to replace the boot message indicating the kernel version
-	  and the driver version with a custom message. This is useful on appliances
-	  where a simple 'Starting system' message can be enough to stop a customer
-	  from worrying.
-
-	  If you say 'Y' here, you'll be able to choose a message yourself. Otherwise,
-	  say 'N' and keep the default message with the version.
-
-config PANEL_BOOT_MESSAGE
-	depends on PANEL_CHANGE_MESSAGE="y"
-	string "New initialization message"
-	default ""
-	---help---
-	  This allows you to replace the boot message indicating the kernel version
-	  and the driver version with a custom message. This is useful on appliances
-	  where a simple 'Starting system' message can be enough to stop a customer
-	  from worrying.
-
-	  An empty message will only clear the display at driver init time. Any other
-	  printf()-formatted message is valid with newline and escape codes.
-
-endif # PANEL
+	  Control Aspeed ast2400/2500 HOST LPC to BMC mappings through
+	  ioctl()s, the driver also provides a read/write interface to a BMC ram
+	  region where the host LPC read/write region can be buffered.
 
 
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/eeprom/Kconfig"

+ 3 - 2
drivers/misc/Makefile

@@ -37,7 +37,6 @@ obj-y				+= eeprom/
 obj-y				+= cb710/
 obj-y				+= cb710/
 obj-$(CONFIG_SPEAR13XX_PCIE_GADGET)	+= spear13xx_pcie_gadget.o
 obj-$(CONFIG_SPEAR13XX_PCIE_GADGET)	+= spear13xx_pcie_gadget.o
 obj-$(CONFIG_VMWARE_BALLOON)	+= vmw_balloon.o
 obj-$(CONFIG_VMWARE_BALLOON)	+= vmw_balloon.o
-obj-$(CONFIG_ARM_CHARLCD)	+= arm-charlcd.o
 obj-$(CONFIG_PCH_PHUB)		+= pch_phub.o
 obj-$(CONFIG_PCH_PHUB)		+= pch_phub.o
 obj-y				+= ti-st/
 obj-y				+= ti-st/
 obj-y				+= lis3lv02d/
 obj-y				+= lis3lv02d/
@@ -53,7 +52,7 @@ obj-$(CONFIG_GENWQE)		+= genwqe/
 obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_CXL_BASE)		+= cxl/
-obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
@@ -62,6 +61,8 @@ lkdtm-$(CONFIG_LKDTM)		+= lkdtm_perms.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_rodata_objcopy.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_rodata_objcopy.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_usercopy.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_usercopy.o
 
 
+KCOV_INSTRUMENT_lkdtm_rodata.o	:= n
+
 OBJCOPYFLAGS :=
 OBJCOPYFLAGS :=
 OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
 OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
 			--set-section-flags .text=alloc,readonly \
 			--set-section-flags .text=alloc,readonly \

+ 266 - 0
drivers/misc/aspeed-lpc-ctrl.c

@@ -0,0 +1,266 @@
+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+
+#include <linux/aspeed-lpc-ctrl.h>
+
+#define DEVICE_NAME	"aspeed-lpc-ctrl"
+
+#define HICR7 0x8
+#define HICR8 0xc
+
+struct aspeed_lpc_ctrl {
+	struct miscdevice	miscdev;
+	struct regmap		*regmap;
+	phys_addr_t		mem_base;
+	resource_size_t		mem_size;
+	u32		pnor_size;
+	u32		pnor_base;
+};
+
+static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file)
+{
+	return container_of(file->private_data, struct aspeed_lpc_ctrl,
+			miscdev);
+}
+
+static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
+	unsigned long vsize = vma->vm_end - vma->vm_start;
+	pgprot_t prot = vma->vm_page_prot;
+
+	if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size)
+		return -EINVAL;
+
+	/* ast2400/2500 AHB accesses are not cache coherent */
+	prot = pgprot_noncached(prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+		(lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
+		vsize, prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
+		unsigned long param)
+{
+	struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
+	void __user *p = (void __user *)param;
+	struct aspeed_lpc_ctrl_mapping map;
+	u32 addr;
+	u32 size;
+	long rc;
+
+	if (copy_from_user(&map, p, sizeof(map)))
+		return -EFAULT;
+
+	if (map.flags != 0)
+		return -EINVAL;
+
+	switch (cmd) {
+	case ASPEED_LPC_CTRL_IOCTL_GET_SIZE:
+		/* The flash windows don't report their size */
+		if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY)
+			return -EINVAL;
+
+		/* Support more than one window id in the future */
+		if (map.window_id != 0)
+			return -EINVAL;
+
+		map.size = lpc_ctrl->mem_size;
+
+		return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0;
+	case ASPEED_LPC_CTRL_IOCTL_MAP:
+
+		/*
+		 * The top half of HICR7 is the MSB of the BMC address of the
+		 * mapping.
+		 * The bottom half of HICR7 is the MSB of the HOST LPC
+		 * firmware space address of the mapping.
+		 *
+		 * The 1 bits in the top of half of HICR8 represent the bits
+		 * (in the requested address) that should be ignored and
+		 * replaced with those from the top half of HICR7.
+		 * The 1 bits in the bottom half of HICR8 represent the bits
+		 * (in the requested address) that should be kept and pass
+		 * into the BMC address space.
+		 */
+
+		/*
+		 * It doesn't make sense to talk about a size or offset with
+		 * low 16 bits set. Both HICR7 and HICR8 talk about the top 16
+		 * bits of addresses and sizes.
+		 */
+
+		if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff))
+			return -EINVAL;
+
+		/*
+		 * Because of the way the masks work in HICR8 offset has to
+		 * be a multiple of size.
+		 */
+		if (map.offset & (map.size - 1))
+			return -EINVAL;
+
+		if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) {
+			addr = lpc_ctrl->pnor_base;
+			size = lpc_ctrl->pnor_size;
+		} else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) {
+			addr = lpc_ctrl->mem_base;
+			size = lpc_ctrl->mem_size;
+		} else {
+			return -EINVAL;
+		}
+
+		/* Check overflow first! */
+		if (map.offset + map.size < map.offset ||
+			map.offset + map.size > size)
+			return -EINVAL;
+
+		if (map.size == 0 || map.size > size)
+			return -EINVAL;
+
+		addr += map.offset;
+
+		/*
+		 * addr (host lpc address) is safe regardless of values. This
+		 * simply changes the address the host has to request on its
+		 * side of the LPC bus. This cannot impact the hosts own
+		 * memory space by surprise as LPC specific accessors are
+		 * required. The only strange thing that could be done is
+		 * setting the lower 16 bits but the shift takes care of that.
+		 */
+
+		rc = regmap_write(lpc_ctrl->regmap, HICR7,
+				(addr | (map.addr >> 16)));
+		if (rc)
+			return rc;
+
+		return regmap_write(lpc_ctrl->regmap, HICR8,
+			(~(map.size - 1)) | ((map.size >> 16) - 1));
+	}
+
+	return -EINVAL;
+}
+
+static const struct file_operations aspeed_lpc_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.mmap		= aspeed_lpc_ctrl_mmap,
+	.unlocked_ioctl	= aspeed_lpc_ctrl_ioctl,
+};
+
+static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
+{
+	struct aspeed_lpc_ctrl *lpc_ctrl;
+	struct device_node *node;
+	struct resource resm;
+	struct device *dev;
+	int rc;
+
+	dev = &pdev->dev;
+
+	lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL);
+	if (!lpc_ctrl)
+		return -ENOMEM;
+
+	node = of_parse_phandle(dev->of_node, "flash", 0);
+	if (!node) {
+		dev_err(dev, "Didn't find host pnor flash node\n");
+		return -ENODEV;
+	}
+
+	rc = of_address_to_resource(node, 1, &resm);
+	of_node_put(node);
+	if (rc) {
+		dev_err(dev, "Couldn't address to resource for flash\n");
+		return rc;
+	}
+
+	lpc_ctrl->pnor_size = resource_size(&resm);
+	lpc_ctrl->pnor_base = resm.start;
+
+	dev_set_drvdata(&pdev->dev, lpc_ctrl);
+
+	node = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (!node) {
+		dev_err(dev, "Didn't find reserved memory\n");
+		return -EINVAL;
+	}
+
+	rc = of_address_to_resource(node, 0, &resm);
+	of_node_put(node);
+	if (rc) {
+		dev_err(dev, "Couldn't address to resource for reserved memory\n");
+		return -ENOMEM;
+	}
+
+	lpc_ctrl->mem_size = resource_size(&resm);
+	lpc_ctrl->mem_base = resm.start;
+
+	lpc_ctrl->regmap = syscon_node_to_regmap(
+			pdev->dev.parent->of_node);
+	if (IS_ERR(lpc_ctrl->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+
+	lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
+	lpc_ctrl->miscdev.name = DEVICE_NAME;
+	lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
+	lpc_ctrl->miscdev.parent = dev;
+	rc = misc_register(&lpc_ctrl->miscdev);
+	if (rc)
+		dev_err(dev, "Unable to register device\n");
+	else
+		dev_info(dev, "Loaded at %pr\n", &resm);
+
+	return rc;
+}
+
+static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
+{
+	struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&lpc_ctrl->miscdev);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_lpc_ctrl_match[] = {
+	{ .compatible = "aspeed,ast2400-lpc-ctrl" },
+	{ .compatible = "aspeed,ast2500-lpc-ctrl" },
+	{ },
+};
+
+static struct platform_driver aspeed_lpc_ctrl_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_lpc_ctrl_match,
+	},
+	.probe = aspeed_lpc_ctrl_probe,
+	.remove = aspeed_lpc_ctrl_remove,
+};
+
+module_platform_driver(aspeed_lpc_ctrl_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
+MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings");

+ 7 - 0
drivers/misc/ds1682.c

@@ -227,9 +227,16 @@ static const struct i2c_device_id ds1682_id[] = {
 };
 };
 MODULE_DEVICE_TABLE(i2c, ds1682_id);
 MODULE_DEVICE_TABLE(i2c, ds1682_id);
 
 
+static const struct of_device_id ds1682_of_match[] = {
+	{ .compatible = "dallas,ds1682", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ds1682_of_match);
+
 static struct i2c_driver ds1682_driver = {
 static struct i2c_driver ds1682_driver = {
 	.driver = {
 	.driver = {
 		.name = "ds1682",
 		.name = "ds1682",
+		.of_match_table = ds1682_of_match,
 	},
 	},
 	.probe = ds1682_probe,
 	.probe = ds1682_probe,
 	.remove = ds1682_remove,
 	.remove = ds1682_remove,

+ 57 - 0
drivers/misc/eeprom/idt_89hpesx.c

@@ -1541,12 +1541,69 @@ static const struct i2c_device_id idt_ids[] = {
 };
 };
 MODULE_DEVICE_TABLE(i2c, idt_ids);
 MODULE_DEVICE_TABLE(i2c, idt_ids);
 
 
+static const struct of_device_id idt_of_match[] = {
+	{ .compatible = "idt,89hpes8nt2", },
+	{ .compatible = "idt,89hpes12nt3", },
+
+	{ .compatible = "idt,89hpes24nt6ag2", },
+	{ .compatible = "idt,89hpes32nt8ag2", },
+	{ .compatible = "idt,89hpes32nt8bg2", },
+	{ .compatible = "idt,89hpes12nt12g2", },
+	{ .compatible = "idt,89hpes16nt16g2", },
+	{ .compatible = "idt,89hpes24nt24g2", },
+	{ .compatible = "idt,89hpes32nt24ag2", },
+	{ .compatible = "idt,89hpes32nt24bg2", },
+
+	{ .compatible = "idt,89hpes12n3", },
+	{ .compatible = "idt,89hpes12n3a", },
+	{ .compatible = "idt,89hpes24n3", },
+	{ .compatible = "idt,89hpes24n3a", },
+
+	{ .compatible = "idt,89hpes32h8", },
+	{ .compatible = "idt,89hpes32h8g2", },
+	{ .compatible = "idt,89hpes48h12", },
+	{ .compatible = "idt,89hpes48h12g2", },
+	{ .compatible = "idt,89hpes48h12ag2", },
+	{ .compatible = "idt,89hpes16h16", },
+	{ .compatible = "idt,89hpes22h16", },
+	{ .compatible = "idt,89hpes22h16g2", },
+	{ .compatible = "idt,89hpes34h16", },
+	{ .compatible = "idt,89hpes34h16g2", },
+	{ .compatible = "idt,89hpes64h16", },
+	{ .compatible = "idt,89hpes64h16g2", },
+	{ .compatible = "idt,89hpes64h16ag2", },
+
+	{ .compatible = "idt,89hpes12t3g2", },
+	{ .compatible = "idt,89hpes24t3g2", },
+
+	{ .compatible = "idt,89hpes16t4", },
+	{ .compatible = "idt,89hpes4t4g2", },
+	{ .compatible = "idt,89hpes10t4g2", },
+	{ .compatible = "idt,89hpes16t4g2", },
+	{ .compatible = "idt,89hpes16t4ag2", },
+	{ .compatible = "idt,89hpes5t5", },
+	{ .compatible = "idt,89hpes6t5", },
+	{ .compatible = "idt,89hpes8t5", },
+	{ .compatible = "idt,89hpes8t5a", },
+	{ .compatible = "idt,89hpes24t6", },
+	{ .compatible = "idt,89hpes6t6g2", },
+	{ .compatible = "idt,89hpes24t6g2", },
+	{ .compatible = "idt,89hpes16t7", },
+	{ .compatible = "idt,89hpes32t8", },
+	{ .compatible = "idt,89hpes32t8g2", },
+	{ .compatible = "idt,89hpes48t12", },
+	{ .compatible = "idt,89hpes48t12g2", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, idt_of_match);
+
 /*
 /*
  * idt_driver - IDT 89HPESx driver structure
  * idt_driver - IDT 89HPESx driver structure
  */
  */
 static struct i2c_driver idt_driver = {
 static struct i2c_driver idt_driver = {
 	.driver = {
 	.driver = {
 		.name = IDT_NAME,
 		.name = IDT_NAME,
+		.of_match_table = idt_of_match,
 	},
 	},
 	.probe = idt_probe,
 	.probe = idt_probe,
 	.remove = idt_remove,
 	.remove = idt_remove,

Some files were not shown because too many files changed in this diff