Index: share/man/man4/Makefile
===================================================================
--- share/man/man4/Makefile	(revision 287980)
+++ share/man/man4/Makefile	(working copy)
@@ -107,6 +107,7 @@
 	cxgb.4 \
 	cxgbe.4 \
 	cy.4 \
+	cyapa.4 \
 	da.4 \
 	dc.4 \
 	dcons.4 \
@@ -186,6 +187,7 @@
 	icmp6.4 \
 	ida.4 \
 	ifmib.4 \
+	ig4.4 \
 	igb.4 \
 	igmp.4 \
 	iic.4 \
@@ -208,6 +210,7 @@
 	ipw.4 \
 	ipwfw.4 \
 	isci.4 \
+	isl.4 \
 	isp.4 \
 	ispfw.4 \
 	iwi.4 \
Index: share/man/man4/cyapa.4
===================================================================
--- share/man/man4/cyapa.4	(revision 0)
+++ share/man/man4/cyapa.4	(working copy)
@@ -0,0 +1,200 @@
+.\" Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 25, 2015
+.Dt CYAPA 4
+.Os
+.Sh NAME
+.Nm cyapa
+.Nd Cypress APA trackpad with I2C interface driver
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines into
+the kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device cyapa"
+.Cd "device ig4"
+.Cd "device smbus"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+cyapa_load="YES"
+ig4_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Cypress APA trackpad.
+It emulates the IntelliMouse PS/2 protocol.
+It supports basic mouse ioctls, so that
+.Xr moused 8
+is supported properly.
+.Ss Trackpad layout
+.Bd -literal
+                   2/3               1/3
+          +--------------------+------------+
+          |                    |   Middle   |
+          |                    |   Button   |
+          |       Left         |            |
+          |      Button        +------------+
+          |                    |   Right    |
+          |                    |   Button   |
+          +--------------------+............|
+          |     Thumb/Button Area           | 15%
+          +---------------------------------+
+.Ed
+.Ss Trackpad features
+.Bl -tag -width 8n
+.It Va Two finger scrolling
+Use two fingers for Z axis scrolling.
+.It Va Button down/second finger
+While one finger clicks and holds down the touchpad, the second finger can be
+used to move the mouse cursor.
+This can be useful for drawing or selecting text.
+.It Va Thumb/Button area
+The lower 15% of the trackpad will not affect the mouse cursor position.
+This allows for high precision clicking, by controlling the cursor with the
+index finger and pushing/holding the pad down with the thumb.
+.It Va Trackpad button
+Push physical button.
+The left two thirds of the pad issues a LEFT button event.
+The upper right corner issues a MIDDLE button event.
+The lower right corner issues a RIGHT button.
+Optionally, tap to click can be enabled (see below).
+.El
+.Sh SYSCTL VARIABLES
+These
+.Xr sysctl 8
+variables are available:
+.Bl -tag -width 8n
+.It Va debug.cyapa_idle_freq
+Scan frequency in idle mode, the default is 1.
+.It Va debug.cyapa_slow_freq
+Scan frequency in slow mode, the default is 20.
+.It Va debug.cyapa_norm_freq
+Scan frequency in normal mode, the default is 100.
+.It Va debug.cyapa_minpressure
+Minimum pressure to detect a finger, the default is 12.
+.It Va debug.cyapa_enable_tapclick
+Controls tap to click.
+Possible values:
+.Bl -tag -width 8n
+.It 0
+Tap to click is disabled.
+This is the default value.
+.It 1
+Tap to click always generates a left mouse button event.
+.It 2
+Tap to click generates left mouse button event if the left 2/3rds of the pad
+are tapped and a right mouse button event otherwise.
+.It 3
+Tap to click generates mouse button events as if the physical button was
+pressed (see
+.Sx DESCRIPTION
+above).
+.El
+.It Va debug.cyapa_tapclick_min_ticks
+Minimum tap duration in ticks to create a click, the default is 1.
+.It Va debug.cyapa_tapclick_max_ticks
+Maximum tap duration in ticks to create a click, the default is 8.
+.It Va debug.cyapa_move_min_ticks
+Minimum ticks before cursor movement occurs, the default is 4.
+.It Va debug.cyapa_scroll_wait_ticks
+Ticks to wait before starting to scroll, the default is 0.
+.It Va debug.cyapa_scroll_stick_ticks
+Ticks while preventing cursor movement on single finger after scroll,
+the default is 15.
+.It Va debug.cyapa_thumbarea_percent
+Size of bottom thumb area in percent, the default is 15.
+.It Va debug.cyapa_debug
+Setting this to a non-zero value enables debug output to console and syslog,
+the default is 0.
+.It Va debug.cyapa_reset
+Setting this to a non-zero value reinitializes the device.
+The sysctl resets to zero immediately.
+.El
+.Sh FILES
+.Nm
+creates
+.Pa /dev/cyapa0 ,
+which presents the mouse as an
+.Ar IntelliMouse PS/2
+device.
+It supports
+.Xr moused 8
+levels 0 through 2, level 1 is used by default.
+.Sh EXAMPLES
+To use
+.Nm
+with
+.Xr moused 8 ,
+add the following lines to the
+.Xr rc.conf 5
+file:
+.Pp
+.Dl moused_enable="YES"
+.Dl moused_port="/dev/cyapa0"
+.Pp
+If vertical scrolling is not desired, add
+.Pp
+.Dl moused_flags="-l0"
+.Pp
+to
+.Xr rc.conf 5 .
+.Pp
+Enable tap to click for the left and the right mouse button and
+disable the thumb area by adding these lines to the
+.Xr sysctl.conf 5
+file:
+.Pp
+.Dl debug.cyapa_thumbarea_percent=0
+.Dl debug.cyapa_enable_tapclick=2
+.Sh SEE ALSO
+.Xr ig4 4 ,
+.Xr moused 4 ,
+.Xr smbus 4 ,
+.Xr sysmouse 4
+.Sh AUTHORS
+.An -nosplit
+The original
+.Nm
+driver was written for DragonFly BSD by
+.An Matthew Dillon .
+.Pp
+It has been ported, modified, and enhanced for
+.Fx
+by
+.An Michael Gmelin Aq Mt freebsd@grem.de .
+.Pp
+This manual page was written by
+.An Michael Gmelin Aq Mt freebsd@grem.de .
+.Sh BUGS
+The
+.Nm
+driver detects the device based on its I2C address (0x67).
+This might have unforeseen consequences if the initialization sequence
+is sent to an unknown device at that address.

Property changes on: share/man/man4/cyapa.4
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: share/man/man4/ig4.4
===================================================================
--- share/man/man4/ig4.4	(revision 0)
+++ share/man/man4/ig4.4	(working copy)
@@ -0,0 +1,79 @@
+.\" Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 30, 2015
+.Dt IG4 4
+.Os
+.Sh NAME
+.Nm ig4
+.Nd Intel(R) fourth generation mobile CPU integrated I2C SMBus driver
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines into
+the kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device ig4"
+.Cd "device smbus"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ig4_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides access to peripherals attached to an I2C SMB controller.
+.Nm
+supports the SMBus controller found in fourth generation Intel(R) Core(TM)
+processors based on the mobile U-processor line for intelligent systems.
+This includes the i7-4650U, i5-4300U, i3-4010U, and 2980U.
+.Sh SYSCTL VARIABLES
+These
+.Xr sysctl 8
+variables are available:
+.Bl -tag -width "debug.ig4_dump"
+.It Va debug.ig4_dump
+Setting this to a non-zero value dumps controller registers to console and
+syslog once.
+The sysctl resets to zero immediately.
+.El
+.Sh SEE ALSO
+.Xr smb 4 ,
+.Xr smbus 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written for DragonFly BSD by
+.An Matthew Dillon
+and subsequently ported to
+.Fx
+by
+.An Michael Gmelin Aq Mt freebsd@grem.de .
+.Pp
+This manual page was written by
+.An Michael Gmelin Aq Mt freebsd@grem.de .

Property changes on: share/man/man4/ig4.4
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: share/man/man4/isl.4
===================================================================
--- share/man/man4/isl.4	(revision 0)
+++ share/man/man4/isl.4	(working copy)
@@ -0,0 +1,104 @@
+.\" Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 25, 2015
+.Dt ISL 4
+.Os
+.Sh NAME
+.Nm isl
+.Nd Intersil(TM) I2C ISL29018 sensor driver
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines into
+the kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device isl"
+.Cd "device ig4"
+.Cd "device smbus"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+isl_load="YES"
+ig4_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides access to sensor data provided by the Intersil(TM) I2C
+ISL29018 Digital Ambient Light Sensor and Proximity Sensor with Interrupt
+Function.
+Functionality is basic and provided through the
+.Xr sysctl 8
+interface.
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables are available:
+.Bl -tag -width "dev.isl.X.resolution"
+.It Va dev.isl.X.als
+Current ALS (Ambient Light Sensor) readout.
+.It Va dev.isl.X.ir
+Current IR (InfraRed) sensor readout.
+.It Va dev.isl.X.prox
+Current proximity sensor readout.
+.It Va dev.isl.X.resolution
+Current sensor resolution.
+.It Va dev.isl.X.range
+Current sensor range.
+.El
+.Sh EXAMPLES
+.Ss Ambient light sensor read out
+.Bd -literal
+$ sysctl dev.isl.0.als
+dev.isl.0.als: 64
+.Ed
+.Ss Automatically adjust brightness
+This requires the port
+.Pa graphics/intel-backlight
+and only works with laptops using a supported Intel(R) GPU.
+.Bd -literal
+$ pkg install intel-backlight
+$ sh /usr/local/share/examples/intel-backlight/isl_backlight.sh
+.Ed
+.Sh SEE ALSO
+.Xr ig4 4 ,
+.Xr smbus 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Michael Gmelin Aq Mt freebsd@grem.de .
+.Pp
+This manual page was written by
+.An Michael Gmelin Aq Mt freebsd@grem.de .
+.Sh BUGS
+The
+.Nm
+driver detects the device based on its I2C address (0x44).
+This might have unforeseen consequences if the initialization sequence
+is sent to an unknown device at that address.

Property changes on: share/man/man4/isl.4
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: share/man/man4/smb.4
===================================================================
--- share/man/man4/smb.4	(revision 287980)
+++ share/man/man4/smb.4	(working copy)
@@ -1,5 +1,6 @@
 .\" Copyright (c) 1998, Nicolas Souchu
 .\" Copyright (c) 2004, Joerg Wunsch
+.\" Copyright (c) 2015, Michael Gmelin <freebsd@grem.de>
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -25,7 +26,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 6, 2009
+.Dd April 25, 2015
 .Dt SMB 4
 .Os
 .Sh NAME
@@ -36,10 +37,10 @@
 .Sh DESCRIPTION
 The
 .Em smb
-character device driver provides generic i/o to any
+character device driver provides generic I/O to any
 .Xr smbus 4
 instance.
-In order to control SMB devices, use
+To control SMB devices, use
 .Pa /dev/smb?
 with the ioctls described below.
 Any of these ioctl commands takes a pointer to
@@ -49,21 +50,24 @@
 #include <sys/types.h>
 
 struct smbcmd {
-	char cmd;
-	int count;
-	u_char slave;
+	u_char cmd;
+	u_char reserved;
+	u_short op;
 	union {
-		char byte;
-		short word;
-
-		char *byte_ptr;
-		short *word_ptr;
-
-		struct {
-			short sdata;
-			short *rdata;
-		} process;
-	} data;
+		char    byte;
+		char    buf[2];
+		short   word;
+	} wdata;
+	union {
+		char    byte;
+		char    buf[2];
+		short   word;
+	} rdata;
+	int  slave;
+	char *wbuf;     /* use wdata if NULL */
+	int  wcount;
+	char *rbuf;     /* use rdata if NULL */
+	int  rcount;
 };
 .Ed
 .Pp
@@ -70,9 +74,9 @@
 The
 .Fa slave
 field is always used, and provides the address of the
-SMBus slave device to talk to.
+SMBus slave device.
 The slave address is specified in the seven most significant bits
-.Pq i.e. Dq "left-justified" .
+.Pq i.e., Dq "left-justified" .
 The least significant bit of the slave address must be zero.
 .Pp
 .Bl -column ".Dv SMB_QUICK_WRITE" -compact
@@ -79,94 +83,112 @@
 .It Em Ioctl Ta Em Description
 .Pp
 .It Dv SMB_QUICK_WRITE Ta
-The
 .Em QuickWrite
-command just issues the device address with write intent
-to the bus, without transferring any data.
+does not transfer any data.
+It just issues the device address with write intent to the bus.
 .It Dv SMB_QUICK_READ Ta
-The
 .Em QuickRead
-command just issues the device address with read intent
-to the bus, without transferring any data.
+does not transfer any data.
+It just issues the device address with read intent to the bus.
 .It Dv SMB_SENDB Ta
-The
 .Em SendByte
-command sends the byte provided in the
+sends the byte provided in
 .Fa cmd
-field to the device.
+to the device.
 .It Dv SMB_RECVB Ta
-The
 .Em ReceiveByte
-command reads a single byte from the device which will
-be returned in the
-.Fa cmd
-field.
+reads a single byte from the device which is returned in
+.Fa cmd .
 .It Dv SMB_WRITEB Ta
-The
 .Em WriteByte
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, followed by the byte given in
-.Fa data.byte .
+to the device, followed by the byte given in
+.Fa wdata.byte .
 .It Dv SMB_WRITEW Ta
-The
 .Em WriteWord
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, followed by the word given in
-.Fa data.word .
+to the device, followed by the word given in
+.Fa wdata.word .
 Note that the SMBus byte-order is little-endian by definition.
 .It Dv SMB_READB Ta
-The
 .Em ReadByte
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, and then reads one byte of data from
+to the device, then reads one byte of data from
 the device.
-The returned data will be stored in the location pointed to by
-.Fa data.byte_ptr .
+Returned data is stored in
+.Fa rdata.byte .
 .It Dv SMB_READW Ta
-The
 .Em ReadWord
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, and then reads one word of data from
+to the device, then reads one word of data from
 the device.
-The returned data will be stored in the location pointed to by
-.Fa data.word_ptr .
+Returned data is stored in
+.Fa rdata.word .
 .It Dv SMB_PCALL Ta
-The
 .Em ProcedureCall
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, followed by the word provided in
-.Fa data.process.sdata .
-It then reads one word of data from the device, and returns it
-in the location pointed to by
-.Fa data.process.rdata .
+to the device, followed by the word provided in
+.Fa wdata.word .
+It then reads one word of data from the device and returns it
+in
+.Fa rdata.word .
 .It Dv SMB_BWRITE Ta
-The
 .Em BlockWrite
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, followed by
-.Fa count
+to the device, followed by
+.Fa wcount
 bytes of data that are taken from the buffer pointed to by
-.Fa data.byte_ptr .
+.Fa wbuf .
 The SMBus specification mandates that no more than 32 bytes of
-data can be transferred in a single block read or write command.
-This value is available in the constant
+data can be transferred in a single block read or write command,
+but since
+.Xr smbus 4
+is also used to access I2C devices, the limit has been increased
+to 1024.
+This value can be read from the constant
 .Dv SMB_MAXBLOCKSIZE .
 .It Dv SMB_BREAD Ta
-The
 .Em BlockRead
-command first sends the byte from the
+first sends the byte from
 .Fa cmd
-field to the device, and then reads
-.Fa count
+to the device, then reads
+.Fa rcount
 bytes of data that from the device.
-These data will be returned in the buffer pointed to by
-.Fa data.byte_ptr .
+This data is returned in the buffer pointed to by
+.Fa rbuf .
+.It Dv SMB_TRANS Ta
+.Em Trans
+sends an SMB roll-up transaction with flags that also allow it to
+be used for (mostly) I2C pass-through and with 10-bit addresses.
+This function can be utilized to roll up all of the above functions.
+It first sends the byte from
+.Fa cmd
+to the device, followed by
+.Fa wcount
+bytes of data that are taken from the buffer pointed to by
+.Fa wbuf ,
+then reads
+.Fa rcount
+bytes of data that from the device.
+This data is returned in the buffer pointed to by
+.Fa rbuf .
+.Pp
+The following flags are allowed in
+.Fa op :
+.Pp
+.Bd -literal -compact
+SMB_TRANS_NOSTOP  Do not send STOP at end
+SMB_TRANS_NOCMD   Ignore cmd field (do not tx)
+SMB_TRANS_NOCNT   Do not tx or rx count field
+SMB_TRANS_7BIT    Change address mode to 7-bit
+SMB_TRANS_10BIT   Change address mode to 10-bit
+.Ed
 .El
 .Pp
 The
@@ -201,4 +223,6 @@
 .Sh AUTHORS
 This
 manual page was written by
-.An Nicolas Souchu .
+.An Nicolas Souchu
+and extended by
+.An Michael Gmelin Aq freebsd@grem.de .
Index: sys/boot/i386/libi386/biosmem.c
===================================================================
--- sys/boot/i386/libi386/biosmem.c	(revision 287980)
+++ sys/boot/i386/libi386/biosmem.c	(working copy)
@@ -32,6 +32,7 @@
  */
 #include <stand.h>
 #include <machine/pc/bios.h>
+#include "bootstrap.h"
 #include "libi386.h"
 #include "btxv86.h"
 
@@ -38,13 +39,56 @@
 vm_offset_t	memtop, memtop_copyin, high_heap_base;
 uint32_t	bios_basemem, bios_extmem, high_heap_size;
 
-static struct bios_smap smap;
+static struct bios_smap_xattr smap;
 
 /*
+ * Used to track which method was used to set BIOS memory
+ * regions.
+ */
+static uint8_t b_bios_probed;
+#define	B_BASEMEM_E820	0x1
+#define	B_BASEMEM_12	0x2
+#define	B_EXTMEM_E820	0x4
+#define	B_EXTMEM_E801	0x8
+#define	B_EXTMEM_8800	0x10
+
+/*
  * The minimum amount of memory to reserve in bios_extmem for the heap.
  */
 #define	HEAP_MIN	(3 * 1024 * 1024)
 
+/*
+ * Products in this list need quirks to detect
+ * memory correctly. You need both maker and product as
+ * reported by smbios.
+ */
+#define BQ_DISTRUST_E820_EXTMEM	0x1	/* e820 might not return useful
+					   extended memory */
+struct bios_getmem_quirks {
+    const char* bios_vendor;
+    const char*	maker;
+    const char*	product;
+    int		quirk;
+};
+
+static struct bios_getmem_quirks quirks[] = {
+    {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM},
+    {NULL, NULL, NULL, 0}
+};
+
+static int
+bios_getquirks(void)
+{
+    int i;
+
+    for (i=0; quirks[i].quirk != 0; ++i)
+	if (smbios_match(quirks[i].bios_vendor, quirks[i].maker,
+	    quirks[i].product))
+	    return (quirks[i].quirk);
+
+    return (0);
+}
+
 void
 bios_getmem(void)
 {
@@ -56,7 +100,7 @@
 	v86.ctl = V86_FLAGS;
 	v86.addr = 0x15;		/* int 0x15 function 0xe820*/
 	v86.eax = 0xe820;
-	v86.ecx = sizeof(struct bios_smap);
+	v86.ecx = sizeof(struct bios_smap_xattr);
 	v86.edx = SMAP_SIG;
 	v86.es = VTOPSEG(&smap);
 	v86.edi = VTOPOFF(&smap);
@@ -65,11 +109,16 @@
 	    break;
 	/* look for a low-memory segment that's large enough */
 	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
-	    (smap.length >= (512 * 1024)))
+	    (smap.length >= (512 * 1024))) {
 	    bios_basemem = smap.length;
+	    b_bios_probed |= B_BASEMEM_E820;
+	}
+
 	/* look for the first segment in 'extended' memory */
-	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000) &&
+	    !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) {
 	    bios_extmem = smap.length;
+	    b_bios_probed |= B_EXTMEM_E820;
 	}
 
 	/*
@@ -100,6 +149,7 @@
 	v86int();
 	
 	bios_basemem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_BASEMEM_12;
     }
 
     /* Fall back through several compatibility functions for extended memory */
@@ -109,7 +159,30 @@
 	v86.eax = 0xe801;
 	v86int();
 	if (!(V86_CY(v86.efl))) {
-	    bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
+	    /*
+	     * Clear high_heap; it may end up overlapping
+	     * with the segment we're determining here.
+	     * Let the default "steal stuff from top of
+	     * bios_extmem" code below pick up on it.
+	     */
+	    high_heap_size = 0;
+	    high_heap_base = 0;
+
+	    /*
+	     * %cx is the number of 1KiB blocks between 1..16MiB.
+	     * It can only be up to 0x3c00; if it's smaller then
+	     * there's a PC AT memory hole so we can't treat
+	     * it as contiguous.
+	     */
+	    bios_extmem = (v86.ecx & 0xffff) * 1024;
+	    if (bios_extmem == (1024 * 0x3c00))
+		bios_extmem += (v86.edx & 0xffff) * 64 * 1024;
+
+	    /* truncate bios_extmem */
+	    if (bios_extmem > 0x3ff00000)
+		bios_extmem = 0x3ff00000;
+
+	    b_bios_probed |= B_EXTMEM_E801;
 	}
     }
     if (bios_extmem == 0) {
@@ -118,6 +191,7 @@
 	v86.eax = 0x8800;
 	v86int();
 	bios_extmem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_EXTMEM_8800;
     }
 
     /* Set memtop to actual top of memory */
@@ -132,4 +206,36 @@
 	high_heap_size = HEAP_MIN;
 	high_heap_base = memtop - HEAP_MIN;
     }
-}    
+}
+
+static int
+command_biosmem(int argc, char *argv[])
+{
+	int bq = bios_getquirks();
+
+	printf("bios_basemem: 0x%llx\n", (unsigned long long) bios_basemem);
+	printf("bios_extmem: 0x%llx\n", (unsigned long long) bios_extmem);
+	printf("memtop: 0x%llx\n", (unsigned long long) memtop);
+	printf("high_heap_base: 0x%llx\n", (unsigned long long) high_heap_base);
+	printf("high_heap_size: 0x%llx\n", (unsigned long long) high_heap_size);
+	printf("bios_quirks: 0x%02x", bq);
+	if (bq & BQ_DISTRUST_E820_EXTMEM)
+		printf(" BQ_DISTRUST_E820_EXTMEM");
+	printf("\n");
+	printf("b_bios_probed: 0x%02x", (int) b_bios_probed);
+	if (b_bios_probed & B_BASEMEM_E820)
+		printf(" B_BASEMEM_E820");
+	if (b_bios_probed & B_BASEMEM_12)
+		printf(" B_BASEMEM_12");
+	if (b_bios_probed & B_EXTMEM_E820)
+		printf(" B_EXTMEM_E820");
+	if (b_bios_probed & B_EXTMEM_E801)
+		printf(" B_EXTMEM_E801");
+	if (b_bios_probed & B_EXTMEM_8800)
+		printf(" B_EXTMEM_8800");
+	printf("\n");
+
+	return (CMD_OK);
+}
+
+COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem);
Index: sys/conf/files
===================================================================
--- sys/conf/files	(revision 287980)
+++ sys/conf/files	(working copy)
@@ -1210,6 +1210,7 @@
 dev/cy/cy.c			optional cy
 dev/cy/cy_isa.c			optional cy isa
 dev/cy/cy_pci.c			optional cy pci
+dev/cyapa/cyapa.c		optional cyapa smbus
 dev/dc/if_dc.c			optional dc pci
 dev/dc/dcphy.c			optional dc pci
 dev/dc/pnphy.c			optional dc pci
@@ -1433,6 +1434,8 @@
 dev/hwpmc/hwpmc_logging.c	optional hwpmc
 dev/hwpmc/hwpmc_mod.c		optional hwpmc
 dev/hwpmc/hwpmc_soft.c		optional hwpmc
+dev/ichiic/ig4_iic.c		optional ig4 smbus
+dev/ichiic/ig4_pci.c		optional ig4 pci smbus
 dev/ichsmb/ichsmb.c		optional ichsmb
 dev/ichsmb/ichsmb_pci.c		optional ichsmb pci
 dev/ida/ida.c			optional ida
@@ -1524,6 +1527,7 @@
 dev/iscsi_initiator/isc_soc.c	optional iscsi_initiator scbus
 dev/iscsi_initiator/isc_sm.c	optional iscsi_initiator scbus
 dev/iscsi_initiator/isc_subr.c	optional iscsi_initiator scbus
+dev/isl/isl.c			optional isl smbus
 dev/isp/isp.c			optional isp
 dev/isp/isp_freebsd.c		optional isp
 dev/isp/isp_library.c		optional isp
Index: sys/dev/atkbdc/atkbd.c
===================================================================
--- sys/dev/atkbdc/atkbd.c	(revision 287980)
+++ sys/dev/atkbdc/atkbd.c	(working copy)
@@ -51,7 +51,11 @@
 
 static timeout_t	atkbd_timeout;
 static void		atkbd_shutdown_final(void *v);
+static int		atkbd_reset(KBDC kbdc, int flags, int c);
 
+#define HAS_QUIRK(p, q)		(((atkbdc_softc_t *)(p))->quirks & q)
+#define ALLOW_DISABLE_KBD(kbdc)	!HAS_QUIRK(kbdc, KBDC_QUIRK_KEEP_ACTIVATED)
+
 #define DEFAULT_DELAY		0x1  /* 500ms */
 #define DEFAULT_RATE		0x10 /* 14Hz */
 
@@ -1076,6 +1080,39 @@
 #endif
 }
 
+static int
+atkbd_reset(KBDC kbdc, int flags, int c)
+{
+	/* reset keyboard hardware */
+	if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
+		/*
+		 * KEYBOARD ERROR
+		 * Keyboard reset may fail either because the keyboard
+		 * doen't exist, or because the keyboard doesn't pass
+		 * the self-test, or the keyboard controller on the
+		 * motherboard and the keyboard somehow fail to shake hands.
+		 * It is just possible, particularly in the last case,
+		 * that the keyboard controller may be left in a hung state.
+		 * test_controller() and test_kbd_port() appear to bring
+		 * the keyboard controller back (I don't know why and how,
+		 * though.)
+		 */
+		empty_both_buffers(kbdc, 10);
+		test_controller(kbdc);
+		test_kbd_port(kbdc);
+		/*
+		 * We could disable the keyboard port and interrupt... but, 
+		 * the keyboard may still exist (see above). 
+		 */
+		set_controller_command_byte(kbdc,
+		    ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
+		if (bootverbose)
+			printf("atkbd: failed to reset the keyboard.\n");
+		return (EIO);
+	}
+	return (0);
+}
+
 /* local functions */
 
 static int
@@ -1193,13 +1230,14 @@
 		kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
 	} else {
 		/* try to restore the command byte as before */
-		set_controller_command_byte(kbdc, 0xff, c);
+		set_controller_command_byte(kbdc,
+		    ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
 		kbdc_set_device_mask(kbdc, m);
 	}
 #endif
 
 	kbdc_lock(kbdc, FALSE);
-	return err;
+	return (HAS_QUIRK(kbdc, KBDC_QUIRK_IGNORE_PROBE_RESULT) ? 0 : err);
 }
 
 static int
@@ -1242,6 +1280,12 @@
 		return EIO;
 	}
 
+	if (HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
+	    atkbd_reset(kbdc, flags, c)) {
+		kbdc_lock(kbdc, FALSE);
+		return EIO;
+	}
+
 	/* 
 	 * Check if we have an XT keyboard before we attempt to reset it. 
 	 * The procedure assumes that the keyboard and the controller have 
@@ -1286,31 +1330,9 @@
 	if (bootverbose)
 		printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type);
 
-	/* reset keyboard hardware */
-	if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
-		/*
-		 * KEYBOARD ERROR
-		 * Keyboard reset may fail either because the keyboard
-		 * doen't exist, or because the keyboard doesn't pass
-		 * the self-test, or the keyboard controller on the
-		 * motherboard and the keyboard somehow fail to shake hands.
-		 * It is just possible, particularly in the last case,
-		 * that the keyboard controller may be left in a hung state.
-		 * test_controller() and test_kbd_port() appear to bring
-		 * the keyboard controller back (I don't know why and how,
-		 * though.)
-		 */
-		empty_both_buffers(kbdc, 10);
-		test_controller(kbdc);
-		test_kbd_port(kbdc);
-		/*
-		 * We could disable the keyboard port and interrupt... but, 
-		 * the keyboard may still exist (see above). 
-		 */
-		set_controller_command_byte(kbdc, 0xff, c);
+	if (!HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
+	    atkbd_reset(kbdc, flags, c)) {
 		kbdc_lock(kbdc, FALSE);
-		if (bootverbose)
-			printf("atkbd: failed to reset the keyboard.\n");
 		return EIO;
 	}
 
@@ -1330,7 +1352,8 @@
 			 * The XT kbd isn't usable unless the proper scan
 			 * code set is selected. 
 			 */
-			set_controller_command_byte(kbdc, 0xff, c);
+			set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
+			    ? 0xff : KBD_KBD_CONTROL_BITS, c);
 			kbdc_lock(kbdc, FALSE);
 			printf("atkbd: unable to set the XT keyboard mode.\n");
 			return EIO;
@@ -1345,6 +1368,17 @@
 	c |= KBD_TRANSLATION;
 #endif
 
+	/*
+	 * Some keyboards require a SETLEDS command to be sent after
+	 * the reset command before they will send keystrokes to us
+	 */
+	if (HAS_QUIRK(kbdc, KBDC_QUIRK_SETLEDS_ON_INIT) &&
+	    send_kbd_command_and_data(kbdc, KBDC_SET_LEDS, 0) != KBD_ACK) {
+		printf("atkbd: setleds failed\n");
+	}
+	if (!ALLOW_DISABLE_KBD(kbdc))
+	    send_kbd_command(kbdc, KBDC_ENABLE_KBD);
+
 	/* enable the keyboard port and intr. */
 	if (!set_controller_command_byte(kbdc, 
 		KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK,
@@ -1355,7 +1389,9 @@
 		 * This is serious; we are left with the disabled
 		 * keyboard intr. 
 		 */
-		set_controller_command_byte(kbdc, 0xff, c);
+		set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
+		    ? 0xff : (KBD_KBD_CONTROL_BITS | KBD_TRANSLATION |
+			KBD_OVERRIDE_KBD_LOCK), c);
 		kbdc_lock(kbdc, FALSE);
 		printf("atkbd: unable to enable the keyboard port and intr.\n");
 		return EIO;
Index: sys/dev/atkbdc/atkbdc.c
===================================================================
--- sys/dev/atkbdc/atkbdc.c	(revision 287980)
+++ sys/dev/atkbdc/atkbdc.c	(working copy)
@@ -114,6 +114,41 @@
 static int wait_for_aux_data(atkbdc_softc_t *kbdc);
 static int wait_for_aux_ack(atkbdc_softc_t *kbdc);
 
+struct atkbdc_quirks {
+    const char* bios_vendor;
+    const char*	maker;
+    const char*	product;
+    int		quirk;
+};
+
+static struct atkbdc_quirks quirks[] = {
+    {"coreboot", "Acer", "Peppy",
+	KBDC_QUIRK_KEEP_ACTIVATED | KBDC_QUIRK_IGNORE_PROBE_RESULT |
+	KBDC_QUIRK_RESET_AFTER_PROBE | KBDC_QUIRK_SETLEDS_ON_INIT},
+
+    {NULL, NULL, NULL, 0}
+};
+
+#define QUIRK_STR_MATCH(s1, s2) (s1 == NULL || \
+    (s2 != NULL && !strcmp(s1, s2)))
+
+static int
+atkbdc_getquirks(void)
+{
+    int i;
+    char* bios_vendor = getenv("smbios.bios.vendor");
+    char* maker = getenv("smbios.system.maker");
+    char* product = getenv("smbios.system.product");
+
+    for (i=0; quirks[i].quirk != 0; ++i)
+	if (QUIRK_STR_MATCH(quirks[i].bios_vendor, bios_vendor) &&
+	    QUIRK_STR_MATCH(quirks[i].maker, maker) &&
+	    QUIRK_STR_MATCH(quirks[i].product, product))
+		return (quirks[i].quirk);
+
+    return (0);
+}
+
 atkbdc_softc_t
 *atkbdc_get_softc(int unit)
 {
@@ -297,6 +332,7 @@
 #else
 	sc->retry = 5000;
 #endif
+	sc->quirks = atkbdc_getquirks();
 
 	return 0;
 }
@@ -1126,7 +1162,8 @@
 kbdc_set_device_mask(KBDC p, int mask)
 {
     kbdcp(p)->command_mask = 
-	mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS);
+	mask & (((kbdcp(p)->quirks & KBDC_QUIRK_KEEP_ACTIVATED)
+	    ? 0 : KBD_KBD_CONTROL_BITS) | KBD_AUX_CONTROL_BITS);
 }
 
 int
Index: sys/dev/atkbdc/atkbdcreg.h
===================================================================
--- sys/dev/atkbdc/atkbdcreg.h	(revision 287980)
+++ sys/dev/atkbdc/atkbdcreg.h	(working copy)
@@ -202,6 +202,11 @@
     kqueue kbd;			/* keyboard data queue */
     kqueue aux;			/* auxiliary data queue */
     int retry;
+    int quirks;			/* controller doesn't like deactivate */
+#define KBDC_QUIRK_KEEP_ACTIVATED	(1 << 0)
+#define KBDC_QUIRK_IGNORE_PROBE_RESULT	(1 << 1)
+#define KBDC_QUIRK_RESET_AFTER_PROBE	(1 << 2)
+#define KBDC_QUIRK_SETLEDS_ON_INIT	(1 << 3)
 } atkbdc_softc_t; 
 
 enum kbdc_device_ivar {
Index: sys/dev/atkbdc/psm.c
===================================================================
--- sys/dev/atkbdc/psm.c	(revision 287980)
+++ sys/dev/atkbdc/psm.c	(working copy)
@@ -375,6 +375,10 @@
 /* other flags (flags) */
 #define	PSM_FLAGS_FINGERDOWN	0x0001	/* VersaPad finger down */
 
+#define kbdcp(p)			((atkbdc_softc_t *)(p))
+#define ALWAYS_RESTORE_CONTROLLER(kbdc)	!(kbdcp(kbdc)->quirks \
+    & KBDC_QUIRK_KEEP_ACTIVATED)
+
 /* Tunables */
 static int tap_enabled = -1;
 TUNABLE_INT("hw.psm.tap_enabled", &tap_enabled);
@@ -1246,7 +1250,8 @@
 		 * this is CONTROLLER ERROR; I don't know how to recover
 		 * from this error...
 		 */
-		restore_controller(sc->kbdc, command_byte);
+		if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+			restore_controller(sc->kbdc, command_byte);
 		printf("psm%d: unable to set the command byte.\n", unit);
 		endprobe(ENXIO);
 	}
@@ -1285,7 +1290,8 @@
 		recover_from_error(sc->kbdc);
 		if (sc->config & PSM_CONFIG_IGNPORTERROR)
 			break;
-		restore_controller(sc->kbdc, command_byte);
+		if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+			restore_controller(sc->kbdc, command_byte);
 		if (verbose)
 			printf("psm%d: the aux port is not functioning (%d).\n",
 			    unit, i);
@@ -1308,7 +1314,8 @@
 		 */
 		if (!reset_aux_dev(sc->kbdc)) {
 			recover_from_error(sc->kbdc);
-			restore_controller(sc->kbdc, command_byte);
+			if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+				restore_controller(sc->kbdc, command_byte);
 			if (verbose)
 				printf("psm%d: failed to reset the aux "
 				    "device.\n", unit);
@@ -1330,7 +1337,8 @@
 	if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
 		/* MOUSE ERROR */
 		recover_from_error(sc->kbdc);
-		restore_controller(sc->kbdc, command_byte);
+		if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+			restore_controller(sc->kbdc, command_byte);
 		if (verbose)
 			printf("psm%d: failed to enable the aux device.\n",
 			    unit);
@@ -1352,7 +1360,8 @@
 	/* verify the device is a mouse */
 	sc->hw.hwid = get_aux_id(sc->kbdc);
 	if (!is_a_mouse(sc->hw.hwid)) {
-		restore_controller(sc->kbdc, command_byte);
+		if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+			restore_controller(sc->kbdc, command_byte);
 		if (verbose)
 			printf("psm%d: unknown device type (%d).\n", unit,
 			    sc->hw.hwid);
@@ -1458,7 +1467,8 @@
 		 * this is CONTROLLER ERROR; I don't know the proper way to
 		 * recover from this error...
 		 */
-		restore_controller(sc->kbdc, command_byte);
+		if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+			restore_controller(sc->kbdc, command_byte);
 		printf("psm%d: unable to set the command byte.\n", unit);
 		endprobe(ENXIO);
 	}
Index: sys/dev/cyapa/cyapa.c
===================================================================
--- sys/dev/cyapa/cyapa.c	(revision 0)
+++ sys/dev/cyapa/cyapa.c	(working copy)
@@ -0,0 +1,1702 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported,
+ * modified and enhanced for FreeBSD by Michael Gmelin <freebsd@grem.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * CYAPA - Cypress APA trackpad with I2C Interface driver
+ *
+ * Based on DragonFlyBSD's cyapa driver, which referenced the linux
+ * cyapa.c driver to figure out the bootstrapping and commands.
+ *
+ * Unable to locate any datasheet for the device.
+ *
+ *
+ * Trackpad layout:
+ *
+ *                2/3               1/3
+ *       +--------------------+------------+
+ *       |                    |   Middle   |
+ *       |                    |   Button   |
+ *       |       Left         |            |
+ *       |      Button        +------------+
+ *       |                    |   Right    |
+ *       |                    |   Button   |
+ *       +--------------------+............|
+ *       |     Thumb/Button Area           | 15%
+ *       +---------------------------------+
+ *
+ *
+ *                             FEATURES
+ *
+ * IMPS/2 emulation       - Emulates the IntelliMouse protocol.
+ *
+ * Jitter supression      - Implements 2-pixel hysteresis with memory.
+ *
+ * Jump detecion          - Detect jumps caused by touchpad.
+ *
+ * Two finger scrolling   - Use two fingers for Z axis scrolling.
+ *
+ * Button down/2nd finger - While one finger clicks and holds down the
+ *                          touchpad, the second one can be used to move
+ *                          the mouse cursor. Useful for drawing or
+ *                          selecting text.
+ *
+ * Thumb/Button Area      - The lower 15%* of the trackpad will not affect
+ *                          the mouse cursor position. This allows for high
+ *                          precision clicking, by controlling the cursor
+ *                          with the index finger and pushing/holding the
+ *                          pad down with the thumb.
+ *                          * can be changed using sysctl
+ *
+ * Track-pad button       - Push physical button. Left 2/3rds of the pad
+ *                          will issue a LEFT button event, upper right
+ *                          corner will issue a MIDDLE button event,
+ *                          lower right corner will issue a RIGHT button
+ *                          event. Optional tap support can be enabled
+ *                          and configured using sysctl.
+ *
+ *                              WARNINGS
+ *
+ * These trackpads get confused when three or more fingers are down on the
+ * same horizontal axis and will start to glitch the finger detection.
+ * Removing your hand for a few seconds will allow the trackpad to
+ * recalibrate.  Generally speaking, when using three or more fingers
+ * please try to place at least one finger off-axis (a little above or
+ * below) the other two.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/event.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/lockmgr.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mouse.h>
+#include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <dev/smbus/smbconf.h>
+#include <dev/smbus/smbus.h>
+#include <dev/cyapa/cyapa.h>
+
+#include "smbus_if.h"
+#include "bus_if.h"
+#include "device_if.h"
+
+#define CYAPA_BUFSIZE	128			/* power of 2 */
+#define CYAPA_BUFMASK	(CYAPA_BUFSIZE - 1)
+
+#define ZSCALE		15
+
+#define TIME_TO_IDLE	(hz * 10)
+#define TIME_TO_RESET	(hz * 3)
+
+static MALLOC_DEFINE(M_CYAPA, "cyapa", "CYAPA device data");
+
+struct cyapa_fifo {
+	int	rindex;
+	int	windex;
+	char	buf[CYAPA_BUFSIZE];
+};
+
+struct cyapa_softc {
+	device_t dev;
+	int	count;			/* >0 if device opened */
+	int	addr;
+	struct cdev *devnode;
+	struct selinfo selinfo;
+	struct mtx mutex;
+
+	int	cap_resx;
+	int	cap_resy;
+	int	cap_phyx;
+	int	cap_phyy;
+	uint8_t	cap_buttons;
+
+	int	detaching;		/* driver is detaching */
+	int	poll_thread_running;	/* poll thread is running */
+
+	/* PS/2 mouse emulation */
+	int	track_x;		/* current tracking */
+	int	track_y;
+	int	track_z;
+	int	track_z_ticks;
+	uint16_t track_but;
+	char	track_id;		/* first finger id */
+	int	track_nfingers;
+	int	delta_x;		/* accumulation -> report */
+	int	delta_y;
+	int	delta_z;
+	int	fuzz_x;
+	int	fuzz_y;
+	int	fuzz_z;
+	int	touch_x;		/* touch down coordinates */
+	int	touch_y;
+	int	touch_z;
+	int	finger1_ticks;
+	int	finger2_ticks;
+	int	finger3_ticks;
+	uint16_t reported_but;
+
+	struct cyapa_fifo rfifo;	/* device->host */
+	struct cyapa_fifo wfifo;	/* host->device */
+	uint8_t	ps2_cmd;		/* active p2_cmd waiting for data */
+	uint8_t ps2_acked;
+	int	active_tick;
+	int	data_signal;
+	int	blocked;
+	int	isselect;
+	int	reporting_mode;		/* 0=disabled 1=enabled */
+	int	scaling_mode;		/* 0=1:1 1=2:1 */
+	int	remote_mode;		/* 0 for streaming mode */
+	int	zenabled;		/* z-axis enabled (mode 1 or 2) */
+	mousehw_t hw;			/* hardware information */
+	mousemode_t mode;		/* mode */
+	int	poll_ticks;
+};
+
+struct cyapa_cdevpriv {
+	struct cyapa_softc *sc;
+};
+
+#define CYPOLL_SHUTDOWN	0x0001
+
+static void cyapa_poll_thread(void *arg);
+static int cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs,
+    int freq);
+static void cyapa_set_power_mode(struct cyapa_softc *sc, int mode);
+
+static int fifo_empty(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
+static size_t fifo_ready(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
+static char *fifo_read(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
+    size_t n);
+static char *fifo_write(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
+    size_t n);
+static uint8_t fifo_read_char(struct cyapa_softc *sc,
+    struct cyapa_fifo *fifo);
+static void fifo_write_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
+    uint8_t c);
+static size_t fifo_space(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
+static void fifo_reset(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
+
+static int cyapa_fuzz(int delta, int *fuzz);
+
+static int cyapa_idle_freq = 1;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_idle_freq, CTLFLAG_RW,
+	    &cyapa_idle_freq, 0, "Scan frequency in idle mode");
+static int cyapa_slow_freq = 20;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_slow_freq, CTLFLAG_RW,
+	    &cyapa_slow_freq, 0, "Scan frequency in slow mode ");
+static int cyapa_norm_freq = 100;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_norm_freq, CTLFLAG_RW,
+	    &cyapa_norm_freq, 0, "Normal scan frequency");
+static int cyapa_minpressure = 12;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_minpressure, CTLFLAG_RW,
+	    &cyapa_minpressure, 0, "Minimum pressure to detect finger");
+static int cyapa_enable_tapclick = 0;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_enable_tapclick, CTLFLAG_RW,
+	    &cyapa_enable_tapclick, 0, "Enable tap to click");
+static int cyapa_tapclick_min_ticks = 1;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_tapclick_min_ticks, CTLFLAG_RW,
+	    &cyapa_tapclick_min_ticks, 0, "Minimum tap duration for click");
+static int cyapa_tapclick_max_ticks = 8;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_tapclick_max_ticks, CTLFLAG_RW,
+	    &cyapa_tapclick_max_ticks, 0, "Maximum tap duration for click");
+static int cyapa_move_min_ticks = 4;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_move_min_ticks, CTLFLAG_RW,
+	    &cyapa_move_min_ticks, 0,
+	    "Minimum ticks before cursor position is changed");
+static int cyapa_scroll_wait_ticks = 0;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_wait_ticks, CTLFLAG_RW,
+	    &cyapa_scroll_wait_ticks, 0,
+	    "Wait N ticks before starting to scroll");
+static int cyapa_scroll_stick_ticks = 15;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_stick_ticks, CTLFLAG_RW,
+	    &cyapa_scroll_stick_ticks, 0,
+	    "Prevent cursor move on single finger for N ticks after scroll");
+static int cyapa_thumbarea_percent = 15;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_thumbarea_percent, CTLFLAG_RW,
+	    &cyapa_thumbarea_percent, 0,
+	    "Size of bottom thumb area in percent");
+
+static int cyapa_debug = 0;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_debug, CTLFLAG_RW,
+	    &cyapa_debug, 0, "Enable debugging");
+static int cyapa_reset = 0;
+SYSCTL_INT(_debug, OID_AUTO, cyapa_reset, CTLFLAG_RW,
+	    &cyapa_reset, 0, "Reset track pad");
+
+static void
+cyapa_lock(struct cyapa_softc *sc)
+{
+
+	mtx_lock(&sc->mutex);
+}
+
+static void
+cyapa_unlock(struct cyapa_softc *sc)
+{
+
+	mtx_unlock(&sc->mutex);
+}
+
+#define	CYAPA_LOCK_ASSERT(sc)	mtx_assert(&(sc)->mutex, MA_OWNED);
+
+/*
+ * Notify if possible receive data ready.  Must be called
+ * with sc->mutex held (cyapa_lock(sc)).
+ */
+static void
+cyapa_notify(struct cyapa_softc *sc)
+{
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	if (sc->data_signal || !fifo_empty(sc, &sc->rfifo)) {
+		KNOTE_LOCKED(&sc->selinfo.si_note, 0);
+		if (sc->blocked || sc->isselect) {
+			if (sc->blocked) {
+			    sc->blocked = 0;
+			    wakeup(&sc->blocked);
+			}
+			if (sc->isselect) {
+			    sc->isselect = 0;
+			    selwakeup(&sc->selinfo);
+			}
+		}
+	}
+}
+
+/*
+ * Initialize the device
+ */
+static int
+init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe)
+{
+	static char bl_exit[] = {
+		0x00, 0xff, 0xa5, 0x00, 0x01,
+		0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+	static char bl_deactivate[] = {
+		0x00, 0xff, 0x3b, 0x00, 0x01,
+		0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+	device_t bus;
+	struct cyapa_boot_regs boot;
+	int error;
+	int retries;
+
+	bus = device_get_parent(dev);	/* smbus */
+
+	/* Get status */
+	error = smbus_trans(bus, addr, CMD_BOOT_STATUS,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, (void *)&boot, sizeof(boot), NULL);
+	if (error)
+		goto done;
+
+	/*
+	 * Bootstrap the device if necessary.  It can take up to 2 seconds
+	 * for the device to fully initialize.
+	 */
+	retries = 20;
+	while ((boot.stat & CYAPA_STAT_RUNNING) == 0 && retries > 0) {
+		if (boot.boot & CYAPA_BOOT_BUSY) {
+			/* Busy, wait loop. */
+		} else if (boot.error & CYAPA_ERROR_BOOTLOADER) {
+			/* Magic */
+			error = smbus_trans(bus, addr, CMD_BOOT_STATUS,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    bl_deactivate, sizeof(bl_deactivate),
+			    NULL, 0, NULL);
+			if (error)
+				goto done;
+		} else {
+			/* Magic */
+			error = smbus_trans(bus, addr, CMD_BOOT_STATUS,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    bl_exit, sizeof(bl_exit), NULL, 0, NULL);
+			if (error)
+				goto done;
+		}
+		pause("cyapab1", (hz * 2) / 10);
+		--retries;
+		error = smbus_trans(bus, addr, CMD_BOOT_STATUS,
+		    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+		    NULL, 0, (void *)&boot, sizeof(boot), NULL);
+		if (error)
+			goto done;
+	}
+
+	if (retries == 0) {
+		device_printf(dev, "Unable to bring device out of bootstrap\n");
+		error = ENXIO;
+		goto done;
+	}
+
+	/* Check identity */
+	if (cap) {
+		error = smbus_trans(bus, addr, CMD_QUERY_CAPABILITIES,
+		    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+		    NULL, 0, (void *)cap, sizeof(*cap), NULL);
+
+		if (strncmp(cap->prod_ida, "CYTRA", 5) != 0) {
+			device_printf(dev, "Product ID \"%5.5s\" mismatch\n",
+			    cap->prod_ida);
+			error = ENXIO;
+		}
+	}
+	error = smbus_trans(bus, addr, CMD_BOOT_STATUS,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, (void *)&boot, sizeof(boot), NULL);
+
+	if (probe == 0)		/* official init */
+		device_printf(dev, "cyapa init status %02x\n", boot.stat);
+	else if (probe == 2)
+		device_printf(dev, "cyapa reset status %02x\n", boot.stat);
+
+done:
+	if (error)
+		device_printf(dev, "Unable to initialize\n");
+	return (error);
+}
+
+static int cyapa_probe(device_t);
+static int cyapa_attach(device_t);
+static int cyapa_detach(device_t);
+static void cyapa_cdevpriv_dtor(void*);
+
+static devclass_t cyapa_devclass;
+
+static device_method_t cyapa_methods[] = {
+	/* device interface */
+	DEVMETHOD(device_probe,		cyapa_probe),
+	DEVMETHOD(device_attach,	cyapa_attach),
+	DEVMETHOD(device_detach,	cyapa_detach),
+
+	DEVMETHOD_END
+};
+
+static driver_t cyapa_driver = {
+	"cyapa",
+	cyapa_methods,
+	sizeof(struct cyapa_softc),
+};
+
+static	d_open_t	cyapaopen;
+static	d_ioctl_t	cyapaioctl;
+static	d_read_t	cyaparead;
+static	d_write_t	cyapawrite;
+static	d_kqfilter_t	cyapakqfilter;
+static	d_poll_t	cyapapoll;
+
+static struct cdevsw cyapa_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_open =	cyapaopen,
+	.d_ioctl =	cyapaioctl,
+	.d_read =	cyaparead,
+	.d_write =	cyapawrite,
+	.d_kqfilter =	cyapakqfilter,
+	.d_poll =	cyapapoll,
+};
+
+static int
+cyapa_probe(device_t dev)
+{
+	struct cyapa_cap cap;
+	int addr;
+	int error;
+
+	addr = smbus_get_addr(dev);
+
+	/*
+	 * 0x67 - cypress trackpad on the acer c720
+	 * (other devices might use other ids).
+	 */
+	if (addr != 0x67)
+		return (ENXIO);
+
+	error = init_device(dev, &cap, addr, 1);
+	if (error != 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "Cypress APA I2C Trackpad");
+
+	return (BUS_PROBE_VENDOR);
+}
+
+static int
+cyapa_attach(device_t dev)
+{
+	struct cyapa_softc *sc;
+	struct cyapa_cap cap;
+	int unit;
+	int addr;
+
+	sc = device_get_softc(dev);
+	sc->reporting_mode = 1;
+
+	unit = device_get_unit(dev);
+	addr = smbus_get_addr(dev);
+
+	if (init_device(dev, &cap, addr, 0))
+		return (ENXIO);
+
+	mtx_init(&sc->mutex, "cyapa", NULL, MTX_DEF);
+
+	sc->dev = dev;
+	sc->addr = addr;
+
+	knlist_init_mtx(&sc->selinfo.si_note, &sc->mutex);
+
+	sc->cap_resx = ((cap.max_abs_xy_high << 4) & 0x0F00) |
+	    cap.max_abs_x_low;
+	sc->cap_resy = ((cap.max_abs_xy_high << 8) & 0x0F00) |
+	    cap.max_abs_y_low;
+	sc->cap_phyx = ((cap.phy_siz_xy_high << 4) & 0x0F00) |
+	    cap.phy_siz_x_low;
+	sc->cap_phyy = ((cap.phy_siz_xy_high << 8) & 0x0F00) |
+	    cap.phy_siz_y_low;
+	sc->cap_buttons = cap.buttons;
+
+	device_printf(dev, "%5.5s-%6.6s-%2.2s buttons=%c%c%c res=%dx%d\n",
+	    cap.prod_ida, cap.prod_idb, cap.prod_idc,
+	    ((cap.buttons & CYAPA_FNGR_LEFT) ? 'L' : '-'),
+	    ((cap.buttons & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
+	    ((cap.buttons & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
+	    sc->cap_resx, sc->cap_resy);
+
+	sc->hw.buttons = 5;
+	sc->hw.iftype = MOUSE_IF_PS2;
+	sc->hw.type = MOUSE_MOUSE;
+	sc->hw.model = MOUSE_MODEL_INTELLI;
+	sc->hw.hwid = addr;
+
+	sc->mode.protocol = MOUSE_PROTO_PS2;
+	sc->mode.rate = 100;
+	sc->mode.resolution = 4;
+	sc->mode.accelfactor = 1;
+	sc->mode.level = 0;
+	sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
+
+	/* Setup input event tracking */
+	cyapa_set_power_mode(sc, CMD_POWER_MODE_IDLE);
+
+	/* Start the polling thread */
+	 kthread_add(cyapa_poll_thread, sc, NULL, NULL,
+	    0, 0, "cyapa-poll");
+
+	sc->devnode = make_dev(&cyapa_cdevsw, unit,
+	    UID_ROOT, GID_WHEEL, 0600, "cyapa%d", unit);
+
+	sc->devnode->si_drv1 = sc;
+
+	return (0);
+}
+
+static int
+cyapa_detach(device_t dev)
+{
+	struct cyapa_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Cleanup poller thread */
+	cyapa_lock(sc);
+	while (sc->poll_thread_running) {
+		sc->detaching = 1;
+		mtx_sleep(&sc->detaching, &sc->mutex, PCATCH, "cyapadet", hz);
+	}
+	cyapa_unlock(sc);
+
+	destroy_dev(sc->devnode);
+
+	knlist_clear(&sc->selinfo.si_note, 0);
+	seldrain(&sc->selinfo);
+	knlist_destroy(&sc->selinfo.si_note);
+
+	mtx_destroy(&sc->mutex);
+
+	return (0);
+}
+
+/*
+ * USER DEVICE I/O FUNCTIONS
+ */
+static int
+cyapaopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+	struct cyapa_cdevpriv *priv;
+	int error;
+
+	priv = malloc(sizeof(*priv), M_CYAPA, M_WAITOK | M_ZERO);
+	priv->sc = dev->si_drv1;
+
+	error = devfs_set_cdevpriv(priv, cyapa_cdevpriv_dtor);
+	if (error == 0) {
+		cyapa_lock(priv->sc);
+		priv->sc->count++;
+		cyapa_unlock(priv->sc);
+	}
+	else
+		free(priv, M_CYAPA);
+
+	return (error);
+}
+
+static void
+cyapa_cdevpriv_dtor(void *data)
+{
+	struct cyapa_cdevpriv *priv;
+
+	priv = data;
+	KASSERT(priv != NULL, ("cyapa cdevpriv should not be NULL!"));
+
+	cyapa_lock(priv->sc);
+	priv->sc->count--;
+	cyapa_unlock(priv->sc);
+
+	free(priv, M_CYAPA);
+}
+
+static int
+cyaparead(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	struct cyapa_softc *sc;
+	int error;
+	int didread;
+	size_t n;
+	char* ptr;
+
+	sc = dev->si_drv1;
+	/* If buffer is empty, load a new event if it is ready */
+	cyapa_lock(sc);
+again:
+	if (fifo_empty(sc, &sc->rfifo) &&
+	    (sc->data_signal || sc->delta_x || sc->delta_y ||
+	     sc->track_but != sc->reported_but)) {
+		uint8_t c0;
+		uint16_t but;
+		int delta_x;
+		int delta_y;
+		int delta_z;
+
+		/* Accumulate delta_x, delta_y */
+		sc->data_signal = 0;
+		delta_x = sc->delta_x;
+		delta_y = sc->delta_y;
+		delta_z = sc->delta_z;
+		if (delta_x > 255) {
+			delta_x = 255;
+			sc->data_signal = 1;
+		}
+		if (delta_x < -256) {
+			delta_x = -256;
+			sc->data_signal = 1;
+		}
+		if (delta_y > 255) {
+			delta_y = 255;
+			sc->data_signal = 1;
+		}
+		if (delta_y < -256) {
+			delta_y = -256;
+			sc->data_signal = 1;
+		}
+		if (delta_z > 255) {
+			delta_z = 255;
+			sc->data_signal = 1;
+		}
+		if (delta_z < -256) {
+			delta_z = -256;
+			sc->data_signal = 1;
+		}
+		but = sc->track_but;
+
+		/* Adjust baseline for next calculation */
+		sc->delta_x -= delta_x;
+		sc->delta_y -= delta_y;
+		sc->delta_z -= delta_z;
+		sc->reported_but = but;
+
+		/*
+		 * Fuzz reduces movement jitter by introducing some
+		 * hysteresis.  It operates without cumulative error so
+		 * if you swish around quickly and return your finger to
+		 * where it started, so to will the mouse.
+		 */
+		delta_x = cyapa_fuzz(delta_x, &sc->fuzz_x);
+		delta_y = cyapa_fuzz(delta_y, &sc->fuzz_y);
+		delta_z = cyapa_fuzz(delta_z, &sc->fuzz_z);
+
+		/*
+		 * Generate report
+		 */
+		c0 = 0;
+		if (delta_x < 0)
+			c0 |= 0x10;
+		if (delta_y < 0)
+			c0 |= 0x20;
+		c0 |= 0x08;
+		if (but & CYAPA_FNGR_LEFT)
+			c0 |= 0x01;
+		if (but & CYAPA_FNGR_MIDDLE)
+			c0 |= 0x04;
+		if (but & CYAPA_FNGR_RIGHT)
+			c0 |= 0x02;
+
+		fifo_write_char(sc, &sc->rfifo, c0);
+		fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_x);
+		fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_y);
+		switch(sc->zenabled) {
+		case 1:
+			/* Z axis all 8 bits */
+			fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_z);
+			break;
+		case 2:
+			/*
+			 * Z axis low 4 bits + 4th button and 5th button
+			 * (high 2 bits must be left 0).  Auto-scale
+			 * delta_z to fit to avoid a wrong-direction
+			 * overflow (don't try to retain the remainder).
+			 */
+			while (delta_z > 7 || delta_z < -8)
+				delta_z >>= 1;
+			c0 = (uint8_t)delta_z & 0x0F;
+			fifo_write_char(sc, &sc->rfifo, c0);
+			break;
+		default:
+			/* basic PS/2 */
+			break;
+		}
+		cyapa_notify(sc);
+	}
+
+	/* Blocking / Non-blocking */
+	error = 0;
+	didread = (uio->uio_resid == 0);
+
+	while ((ioflag & IO_NDELAY) == 0 && fifo_empty(sc, &sc->rfifo)) {
+		if (sc->data_signal)
+			goto again;
+		sc->blocked = 1;
+		error = mtx_sleep(&sc->blocked, &sc->mutex, PCATCH, "cyablk", 0);
+		if (error)
+			break;
+	}
+
+	/* Return any buffered data */
+	while (error == 0 && uio->uio_resid &&
+	    (n = fifo_ready(sc, &sc->rfifo)) > 0) {
+		if (n > uio->uio_resid)
+			n = uio->uio_resid;
+		ptr = fifo_read(sc, &sc->rfifo, 0);
+		cyapa_unlock(sc);
+		error = uiomove(ptr, n, uio);
+		cyapa_lock(sc);
+		if (error)
+			break;
+		fifo_read(sc, &sc->rfifo, n);
+		didread = 1;
+	}
+	cyapa_unlock(sc);
+
+	if (error == 0 && didread == 0) {
+		error = EWOULDBLOCK;
+	}
+	return (didread ? 0 : error);
+}
+
+static int
+cyapawrite(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	struct cyapa_softc *sc;
+	int error;
+	int cmd_completed;
+	size_t n;
+	uint8_t c0;
+	char* ptr;
+
+	sc = dev->si_drv1;
+again:
+	/*
+	 * Copy data from userland.  This will also cross-over the end
+	 * of the fifo and keep filling.
+	 */
+	cyapa_lock(sc);
+	while ((n = fifo_space(sc, &sc->wfifo)) > 0 && uio->uio_resid) {
+		if (n > uio->uio_resid)
+			n = uio->uio_resid;
+		ptr = fifo_write(sc, &sc->wfifo, 0);
+		cyapa_unlock(sc);
+		error = uiomove(ptr, n, uio);
+		cyapa_lock(sc);
+		if (error)
+			break;
+		fifo_write(sc, &sc->wfifo, n);
+	}
+
+	/* Handle commands */
+	cmd_completed = (fifo_ready(sc, &sc->wfifo) != 0);
+	while (fifo_ready(sc, &sc->wfifo) && cmd_completed && error == 0) {
+		if (sc->ps2_cmd == 0)
+			sc->ps2_cmd = fifo_read_char(sc, &sc->wfifo);
+		switch(sc->ps2_cmd) {
+		case 0xE6:
+			/* SET SCALING 1:1 */
+			sc->scaling_mode = 0;
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			break;
+		case 0xE7:
+			/* SET SCALING 2:1 */
+			sc->scaling_mode = 1;
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			break;
+		case 0xE8:
+			/* SET RESOLUTION +1 byte */
+			if (sc->ps2_acked == 0) {
+				sc->ps2_acked = 1;
+				fifo_write_char(sc, &sc->rfifo, 0xFA);
+			}
+			if (fifo_ready(sc, &sc->wfifo) == 0) {
+				cmd_completed = 0;
+				break;
+			}
+			sc->mode.resolution = fifo_read_char(sc, &sc->wfifo);
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			break;
+		case 0xE9:
+			/*
+			 * STATUS REQUEST
+			 *
+			 * byte1:
+			 *	bit 7	0
+			 *	bit 6	Mode	(1=remote mode, 0=stream mode)
+			 *	bit 5	Enable	(data reporting enabled)
+			 *	bit 4	Scaling	(0=1:1 1=2:1)
+			 *	bit 3	0
+			 *	bit 2	LEFT BUTTON    (1 if pressed)
+			 *	bit 1	MIDDLE BUTTON  (1 if pressed)
+			 *	bit 0	RIGHT BUTTON   (1 if pressed)
+			 *
+			 * byte2: resolution counts/mm
+			 * byte3: sample rate
+			 */
+			c0 = 0;
+			if (sc->remote_mode)
+				c0 |= 0x40;
+			if (sc->reporting_mode)
+				c0 |= 0x20;
+			if (sc->scaling_mode)
+				c0 |= 0x10;
+			if (sc->track_but & CYAPA_FNGR_LEFT)
+				c0 |= 0x04;
+			if (sc->track_but & CYAPA_FNGR_MIDDLE)
+				c0 |= 0x02;
+			if (sc->track_but & CYAPA_FNGR_RIGHT)
+				c0 |= 0x01;
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			fifo_write_char(sc, &sc->rfifo, c0);
+			fifo_write_char(sc, &sc->rfifo, 0x00);
+			fifo_write_char(sc, &sc->rfifo, 100);
+			break;
+		case 0xEA:
+			/* Set stream mode and reset movement counters */
+			sc->remote_mode = 0;
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->delta_x = 0;
+			sc->delta_y = 0;
+			sc->delta_z = 0;
+			break;
+		case 0xEB:
+			/*
+			 * Read Data (if in remote mode).  If not in remote
+			 * mode force an event.
+			 */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->data_signal = 1;
+			break;
+		case 0xEC:
+			/* Reset Wrap Mode (ignored) */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			break;
+		case 0xEE:
+			/* Set Wrap Mode (ignored) */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			break;
+		case 0xF0:
+			/* Set Remote Mode */
+			sc->remote_mode = 1;
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->delta_x = 0;
+			sc->delta_y = 0;
+			sc->delta_z = 0;
+			break;
+		case 0xF2:
+			/*
+			 * Get Device ID
+			 *
+			 * If we send 0x00 - normal PS/2 mouse, no Z-axis
+			 *
+			 * If we send 0x03 - Intellimouse, data packet has
+			 * an additional Z movement byte (8 bits signed).
+			 * (also reset movement counters)
+			 *
+			 * If we send 0x04 - Now includes z-axis and the
+			 * 4th and 5th mouse buttons.
+			 */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			switch(sc->zenabled) {
+			case 1:
+				fifo_write_char(sc, &sc->rfifo, 0x03);
+				break;
+			case 2:
+				fifo_write_char(sc, &sc->rfifo, 0x04);
+				break;
+			default:
+				fifo_write_char(sc, &sc->rfifo, 0x00);
+				break;
+			}
+			sc->delta_x = 0;
+			sc->delta_y = 0;
+			sc->delta_z = 0;
+			break;
+		case 0xF3:
+			/*
+			 * Set Sample Rate
+			 *
+			 * byte1: the sample rate
+			 */
+			if (sc->ps2_acked == 0) {
+				sc->ps2_acked = 1;
+				fifo_write_char(sc, &sc->rfifo, 0xFA);
+			}
+			if (fifo_ready(sc, &sc->wfifo) == 0) {
+				cmd_completed = 0;
+				break;
+			}
+			sc->mode.rate = fifo_read_char(sc, &sc->wfifo);
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+
+			/*
+			 * zenabling sequence: 200,100,80 (device id 0x03)
+			 *		       200,200,80 (device id 0x04)
+			 *
+			 * We support id 0x03 (no 4th or 5th button).
+			 * We support id 0x04 (w/ 4th and 5th button).
+			 */
+			if (sc->zenabled == 0 && sc->mode.rate == 200)
+				sc->zenabled = -1;
+			else if (sc->zenabled == -1 && sc->mode.rate == 100)
+				sc->zenabled = -2;
+			else if (sc->zenabled == -1 && sc->mode.rate == 200)
+				sc->zenabled = -3;
+			else if (sc->zenabled == -2 && sc->mode.rate == 80)
+				sc->zenabled = 1;	/* z-axis mode */
+			else if (sc->zenabled == -3 && sc->mode.rate == 80)
+				sc->zenabled = 2;	/* z-axis+but4/5 */
+			if (sc->mode.level)
+				sc->zenabled = 1;
+			break;
+		case 0xF4:
+			/* Enable data reporting.  Only effects stream mode. */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->reporting_mode = 1;
+			break;
+		case 0xF5:
+			/*
+			 * Disable data reporting.  Only effects stream mode
+			 * and is ignored right now.
+			 */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->reporting_mode = 1;
+			break;
+		case 0xF6:
+			/*
+			 * SET DEFAULTS
+			 *
+			 * (reset sampling rate, resolution, scaling and
+			 *  enter stream mode)
+			 */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->mode.rate = 100;
+			sc->mode.resolution = 4;
+			sc->scaling_mode = 0;
+			sc->reporting_mode = 1;
+			sc->remote_mode = 0;
+			sc->delta_x = 0;
+			sc->delta_y = 0;
+			sc->delta_z = 0;
+			/* signal */
+			break;
+		case 0xFE:
+			/*
+			 * RESEND
+			 *
+			 * Force a resend by guaranteeing that reported_but
+			 * differs from track_but.
+			 */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->data_signal = 1;
+			break;
+		case 0xFF:
+			/*
+			 * RESET
+			 */
+			fifo_reset(sc, &sc->rfifo);	/* should we do this? */
+			fifo_reset(sc, &sc->wfifo);	/* should we do this? */
+			fifo_write_char(sc, &sc->rfifo, 0xFA);
+			sc->delta_x = 0;
+			sc->delta_y = 0;
+			sc->delta_z = 0;
+			sc->zenabled = 0;
+			sc->mode.level = 0;
+			break;
+		default:
+			printf("unknown command %02x\n", sc->ps2_cmd);
+			break;
+		}
+		if (cmd_completed) {
+			sc->ps2_cmd = 0;
+			sc->ps2_acked = 0;
+		}
+		cyapa_notify(sc);
+	}
+	cyapa_unlock(sc);
+	if (error == 0 && (cmd_completed || uio->uio_resid))
+		goto again;
+	return (error);
+}
+
+static void cyapafiltdetach(struct knote *);
+static int cyapafilt(struct knote *, long);
+
+static struct filterops cyapa_filtops = {
+	    .f_isfd = 1,
+	    .f_detach = cyapafiltdetach,
+	    .f_event = cyapafilt
+};
+
+static int
+cyapakqfilter(struct cdev *dev, struct knote *kn)
+{
+	struct cyapa_softc *sc;
+	struct knlist *knlist;
+
+	sc = dev->si_drv1;
+
+	switch(kn->kn_filter) {
+	case EVFILT_READ:
+		kn->kn_fop = &cyapa_filtops;
+		kn->kn_hook = (void *)sc;
+		break;
+	default:
+		return (EOPNOTSUPP);
+	}
+	knlist = &sc->selinfo.si_note;
+	knlist_add(knlist, kn, 0);
+
+	return (0);
+}
+
+static int
+cyapapoll(struct cdev *dev, int events, struct thread *td)
+{
+	struct cyapa_softc *sc;
+	int revents;
+
+	sc = dev->si_drv1;
+	revents = 0;
+
+	cyapa_lock(sc);
+	if (events & (POLLIN | POLLRDNORM)) {
+		if (sc->data_signal || !fifo_empty(sc, &sc->rfifo))
+			revents = events & (POLLIN | POLLRDNORM);
+		else {
+			sc->isselect = 1;
+			selrecord(td, &sc->selinfo);
+		}
+	}
+	cyapa_unlock(sc);
+
+	return (revents);
+}
+
+static void
+cyapafiltdetach(struct knote *kn)
+{
+	struct cyapa_softc *sc;
+	struct knlist *knlist;
+
+	sc = (struct cyapa_softc *)kn->kn_hook;
+
+	knlist = &sc->selinfo.si_note;
+	knlist_remove(knlist, kn, 0);
+}
+
+static int
+cyapafilt(struct knote *kn, long hint)
+{
+	struct cyapa_softc *sc;
+	int ready;
+
+	sc = (struct cyapa_softc *)kn->kn_hook;
+
+	cyapa_lock(sc);
+	ready = fifo_ready(sc, &sc->rfifo) || sc->data_signal;
+	cyapa_unlock(sc);
+
+	return (ready);
+}
+
+static int
+cyapaioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
+{
+	struct cyapa_softc *sc;
+	int error;
+
+	sc = dev->si_drv1;
+	error = 0;
+
+	cyapa_lock(sc);
+	switch (cmd) {
+	case MOUSE_GETHWINFO:
+		*(mousehw_t *)data = sc->hw;
+		if (sc->mode.level == 0)
+			((mousehw_t *)data)->model = MOUSE_MODEL_GENERIC;
+		break;
+
+	case MOUSE_GETMODE:
+		*(mousemode_t *)data = sc->mode;
+		((mousemode_t *)data)->resolution =
+		    MOUSE_RES_LOW - sc->mode.resolution;
+		switch (sc->mode.level) {
+		case 0:
+			((mousemode_t *)data)->protocol = MOUSE_PROTO_PS2;
+			((mousemode_t *)data)->packetsize =
+			    MOUSE_PS2_PACKETSIZE;
+			break;
+		case 2:
+			((mousemode_t *)data)->protocol = MOUSE_PROTO_PS2;
+			((mousemode_t *)data)->packetsize =
+			    MOUSE_PS2_PACKETSIZE + 1;
+			break;
+		}
+		break;
+
+	case MOUSE_GETLEVEL:
+		*(int *)data = sc->mode.level;
+		break;
+
+	case MOUSE_SETLEVEL:
+		if ((*(int *)data < 0) &&
+		    (*(int *)data > 2)) {
+			error = EINVAL;
+			break;
+		}
+		sc->mode.level = *(int *)data ? 2 : 0;
+		sc->zenabled = sc->mode.level ? 1 : 0;
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+	cyapa_unlock(sc);
+
+	return (error);
+}
+
+/*
+ * MAJOR SUPPORT FUNCTIONS
+ */
+static void
+cyapa_poll_thread(void *arg)
+{
+	struct cyapa_softc *sc;
+	struct cyapa_regs regs;
+	device_t bus;		/* smbus */
+	int error;
+	int freq;
+	int isidle;
+	int pstate;
+	int npstate;
+	int last_reset;
+
+	sc = arg;
+	freq = cyapa_norm_freq;
+	isidle = 0;
+	pstate = CMD_POWER_MODE_IDLE;
+	last_reset = ticks;
+
+	bus = device_get_parent(sc->dev);
+
+	cyapa_lock(sc);
+	sc->poll_thread_running = 1;
+
+	while (!sc->detaching) {
+		cyapa_unlock(sc);
+		error = smbus_request_bus(bus, sc->dev, SMB_WAIT);
+		if (error == 0) {
+			error = smbus_trans(bus, sc->addr, CMD_DEV_STATUS,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    NULL, 0,
+			    (void *)&regs, sizeof(regs), NULL);
+			if (error == 0) {
+				isidle = cyapa_raw_input(sc, &regs, freq);
+			}
+
+			/*
+			 * For some reason the device can crap-out.  If it
+			 * drops back into bootstrap mode try to reinitialize
+			 * it.
+			 */
+			if (cyapa_reset ||
+			    ((regs.stat & CYAPA_STAT_RUNNING) == 0 &&
+			     (unsigned)(ticks - last_reset) > TIME_TO_RESET)) {
+				cyapa_reset = 0;
+				last_reset = ticks;
+				init_device(sc->dev, NULL, sc->addr, 2);
+			}
+			smbus_release_bus(bus, sc->dev);
+		}
+		pause("cyapw", hz / freq);
+		++sc->poll_ticks;
+
+		if (sc->count == 0) {
+			freq = cyapa_idle_freq;
+			npstate = CMD_POWER_MODE_IDLE;
+		} else if (isidle) {
+			freq = cyapa_slow_freq;
+			npstate = CMD_POWER_MODE_IDLE;
+		} else {
+			freq = cyapa_norm_freq;
+			npstate = CMD_POWER_MODE_FULL;
+		}
+		if (pstate != npstate) {
+			pstate = npstate;
+			cyapa_set_power_mode(sc, pstate);
+			if (cyapa_debug) {
+				switch(pstate) {
+				case CMD_POWER_MODE_OFF:
+					printf("cyapa: power off\n");
+					break;
+				case CMD_POWER_MODE_IDLE:
+					printf("cyapa: power idle\n");
+					break;
+				case CMD_POWER_MODE_FULL:
+					printf("cyapa: power full\n");
+					break;
+				}
+			}
+		}
+
+		cyapa_lock(sc);
+	}
+	sc->poll_thread_running = 0;
+	cyapa_unlock(sc);
+	kthread_exit();
+}
+
+static int
+cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs, int freq)
+{
+	int nfingers;
+	int afingers;	/* actual fingers after culling */
+	int i;
+	int j;
+	int k;
+	int isidle;
+	int thumbarea_begin;
+	int seen_thumb;
+	int x;
+	int y;
+	int z;
+	int newfinger;
+	int lessfingers;
+	int click_x;
+	int click_y;
+	uint16_t but;	/* high bits used for simulated but4/but5 */
+
+	thumbarea_begin = sc->cap_resy -
+	    ((sc->cap_resy *  cyapa_thumbarea_percent) / 100);
+	click_x = click_y = 0;
+
+	/*
+	 * If the device is not running the rest of the status
+	 * means something else, set fingers to 0.
+	 */
+	if ((regs->stat & CYAPA_STAT_RUNNING) == 0) {
+		regs->fngr = 0;
+	}
+
+	/* Process fingers/movement */
+	nfingers = CYAPA_FNGR_NUMFINGERS(regs->fngr);
+	afingers = nfingers;
+
+	if (cyapa_debug) {
+		printf("stat %02x buttons %c%c%c nfngrs=%d ",
+		    regs->stat,
+		    ((regs->fngr & CYAPA_FNGR_LEFT) ? 'L' : '-'),
+		    ((regs->fngr & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
+		    ((regs->fngr & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
+		    nfingers);
+	}
+
+	seen_thumb = 0;
+	for (i = 0; i < afingers; ) {
+		if (cyapa_debug) {
+			printf(" [x=%04d y=%04d p=%d i=%d]",
+			    CYAPA_TOUCH_X(regs, i),
+			    CYAPA_TOUCH_Y(regs, i),
+			    CYAPA_TOUCH_P(regs, i),
+			    regs->touch[i].id);
+		}
+		if ((CYAPA_TOUCH_Y(regs, i) > thumbarea_begin && seen_thumb) ||
+		     CYAPA_TOUCH_P(regs, i) < cyapa_minpressure) {
+			--afingers;
+			if (i < afingers) {
+			    regs->touch[i] = regs->touch[i+1];
+			    continue;
+			}
+		} else {
+			if (CYAPA_TOUCH_Y(regs, i) > thumbarea_begin)
+			    seen_thumb = 1;
+		}
+		++i;
+	}
+	nfingers = afingers;
+
+	/* Tracking for local solutions */
+	cyapa_lock(sc);
+
+	/*
+	 * Track timing for finger-downs.  Used to detect false-3-finger
+	 * button-down.
+	 */
+	switch(afingers) {
+	case 0:
+		break;
+	case 1:
+		if (sc->track_nfingers == 0)
+			sc->finger1_ticks = sc->poll_ticks;
+		break;
+	case 2:
+		if (sc->track_nfingers <= 0)
+			sc->finger1_ticks = sc->poll_ticks;
+		if (sc->track_nfingers <= 1)
+			sc->finger2_ticks = sc->poll_ticks;
+		break;
+	case 3:
+	default:
+		if (sc->track_nfingers <= 0)
+			sc->finger1_ticks = sc->poll_ticks;
+		if (sc->track_nfingers <= 1)
+			sc->finger2_ticks = sc->poll_ticks;
+		if (sc->track_nfingers <= 2)
+			sc->finger3_ticks = sc->poll_ticks;
+		break;
+	}
+	newfinger = sc->track_nfingers < afingers;
+	lessfingers = sc->track_nfingers > afingers;
+	sc->track_nfingers = afingers;
+
+	/*
+	 * Lookup and track finger indexes in the touch[] array.
+	 */
+	if (afingers == 0) {
+		click_x = sc->track_x;
+		click_y = sc->track_y;
+		sc->track_x = -1;
+		sc->track_y = -1;
+		sc->track_z = -1;
+		sc->fuzz_x = 0;
+		sc->fuzz_y = 0;
+		sc->fuzz_z = 0;
+		sc->touch_x = -1;
+		sc->touch_y = -1;
+		sc->touch_z = -1;
+		sc->track_id = -1;
+		sc->track_but = 0;
+		i = 0;
+		j = 0;
+		k = 0;
+	} else {
+		/*
+		 * The id assigned on touch can move around in the array,
+		 * find it.  If that finger is lifted up, assign some other
+		 * finger for mouse tracking and reset track_x and track_y
+		 * to avoid a mouse jump.
+		 *
+		 * If >= 2 fingers are down be sure not to assign i and
+		 * j to the same index.
+		 */
+		for (i = 0; i < nfingers; ++i) {
+			if (sc->track_id == regs->touch[i].id)
+				break;
+		}
+		if (i == nfingers) {
+			i = 0;
+			sc->track_x = -1;
+			sc->track_y = -1;
+			sc->track_z = -1;
+			while (CYAPA_TOUCH_Y(regs, i) >= thumbarea_begin &&
+			    i < nfingers) ++i;
+			if (i == nfingers) {
+				i = 0;
+			}
+			sc->track_id = regs->touch[i].id;
+		}
+		else if ((sc->track_but ||
+		     CYAPA_TOUCH_Y(regs, i) >= thumbarea_begin) &&
+		    newfinger && afingers == 2) {
+			j = regs->touch[0].id == sc->track_id ? 1 : 0;
+			if (CYAPA_TOUCH_Y(regs, j) < thumbarea_begin) {
+			    i = j;
+			    sc->track_x = -1;
+			    sc->track_y = -1;
+			    sc->track_z = -1;
+			    sc->track_id = regs->touch[i].id;
+			}
+		}
+	}
+
+	/* Two finger scrolling - reset after timeout */
+	if (sc->track_z != -1 && afingers != 2 &&
+	    (sc->poll_ticks - sc->track_z_ticks) > cyapa_scroll_stick_ticks) {
+		sc->track_z = -1;
+		sc->track_z_ticks = 0;
+	}
+
+	/* Initiate two finger scrolling */
+	if (!(regs->fngr & CYAPA_FNGR_LEFT) &&
+	    ((afingers && sc->track_z != -1) ||
+	     (afingers == 2 && CYAPA_TOUCH_Y(regs, 0) < thumbarea_begin &&
+	     CYAPA_TOUCH_Y(regs, 1) < thumbarea_begin))) {
+		if (afingers == 2 && (sc->poll_ticks - sc->finger2_ticks)
+		    > cyapa_scroll_wait_ticks) {
+			z = (CYAPA_TOUCH_Y(regs, 0) +
+			    CYAPA_TOUCH_Y(regs, 1)) >> 1;
+			sc->delta_z += z / ZSCALE - sc->track_z;
+			if (sc->track_z == -1) {
+			    sc->delta_z = 0;
+			}
+			if (sc->touch_z == -1)
+			    sc->touch_z = z;	/* not used atm */
+			sc->track_z = z / ZSCALE;
+			sc->track_z_ticks = sc->poll_ticks;
+		}
+	} else if (afingers) {
+		/* Normal pad position reporting */
+		x = CYAPA_TOUCH_X(regs, i);
+		y = CYAPA_TOUCH_Y(regs, i);
+		click_x = x;
+		click_y = y;
+		if (sc->track_x != -1 && sc->track_y < thumbarea_begin &&
+		    (afingers > 1 || (sc->poll_ticks - sc->finger1_ticks)
+		    >= cyapa_move_min_ticks || freq < cyapa_norm_freq)) {
+			sc->delta_x += x - sc->track_x;
+			sc->delta_y -= y - sc->track_y;
+			if (sc->delta_x > sc->cap_resx)
+				sc->delta_x = sc->cap_resx;
+			if (sc->delta_x < -sc->cap_resx)
+				sc->delta_x = -sc->cap_resx;
+			if (sc->delta_y > sc->cap_resx)
+				sc->delta_y = sc->cap_resy;
+			if (sc->delta_y < -sc->cap_resy)
+				sc->delta_y = -sc->cap_resy;
+
+			if (abs(sc->delta_y) > sc->cap_resy / 2 ||
+			    abs(sc->delta_x) > sc->cap_resx / 2) {
+				if (cyapa_debug)
+					printf("Detected jump by %i %i\n",
+					    sc->delta_x, sc->delta_y);
+			    sc->delta_x = sc->delta_y = 0;
+			}
+		}
+		if (sc->touch_x == -1) {
+			sc->touch_x = x;
+			sc->touch_y = y;
+		}
+		sc->track_x = x;
+		sc->track_y = y;
+	}
+
+	/* Select finger (L = 2/3x, M = 1/3u, R = 1/3d) */
+	int is_tapclick = (cyapa_enable_tapclick && lessfingers &&
+	    afingers == 0 && sc->poll_ticks - sc->finger1_ticks
+	    >= cyapa_tapclick_min_ticks &&
+	    sc->poll_ticks - sc->finger1_ticks < cyapa_tapclick_max_ticks);
+
+	if (regs->fngr & CYAPA_FNGR_LEFT || is_tapclick) {
+		if (sc->track_but) {
+			but = sc->track_but;
+		} else if (afingers == 1) {
+			if (click_x < sc->cap_resx * 2 / 3)
+				but = CYAPA_FNGR_LEFT;
+			else if (click_y < sc->cap_resy / 2)
+				but = CYAPA_FNGR_MIDDLE;
+			else
+				but = CYAPA_FNGR_RIGHT;
+		} else if (is_tapclick) {
+			if (click_x < sc->cap_resx * 2 / 3 ||
+			    cyapa_enable_tapclick < 2)
+				but = CYAPA_FNGR_LEFT;
+			else if (click_y < sc->cap_resy / 2 &&
+			    cyapa_enable_tapclick > 2)
+				but = CYAPA_FNGR_MIDDLE;
+			else
+				but = CYAPA_FNGR_RIGHT;
+		} else {
+			but = CYAPA_FNGR_LEFT;
+		}
+	} else {
+		but = 0;
+	}
+
+	/*
+	 * Detect state change from last reported state and
+	 * determine if we have gone idle.
+	 */
+	sc->track_but = but;
+	if (sc->delta_x || sc->delta_y || sc->delta_z ||
+	    sc->track_but != sc->reported_but) {
+		sc->active_tick = ticks;
+		if (sc->remote_mode == 0 && sc->reporting_mode)
+			sc->data_signal = 1;
+		isidle = 0;
+	} else if ((unsigned)(ticks - sc->active_tick) >= TIME_TO_IDLE) {
+		sc->active_tick = ticks - TIME_TO_IDLE; /* prevent overflow */
+		isidle = 1;
+	} else {
+		isidle = 0;
+	}
+	cyapa_notify(sc);
+	cyapa_unlock(sc);
+
+	if (cyapa_debug)
+		printf("%i >> %i << %i\n", isidle, sc->track_id, sc->delta_y);
+	return (isidle);
+}
+
+static void
+cyapa_set_power_mode(struct cyapa_softc *sc, int mode)
+{
+	uint8_t data;
+	device_t bus;
+	int error;
+
+	bus = device_get_parent(sc->dev);
+	error = smbus_request_bus(bus, sc->dev, SMB_WAIT);
+	if (error == 0) {
+		error = smbus_trans(bus, sc->addr, CMD_POWER_MODE,
+		    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+		    NULL, 0, (void *)&data, 1, NULL);
+		data = (data & ~0xFC) | mode;
+		if (error == 0) {
+			error = smbus_trans(bus, sc->addr, CMD_POWER_MODE,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    (void *)&data, 1, NULL, 0, NULL);
+		}
+		smbus_release_bus(bus, sc->dev);
+	}
+}
+
+/*
+ * FIFO FUNCTIONS
+ */
+
+/*
+ * Returns non-zero if the fifo is empty
+ */
+static int
+fifo_empty(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
+{
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	return (fifo->rindex == fifo->windex);
+}
+
+/*
+ * Returns the number of characters available for reading from
+ * the fifo without wrapping the fifo buffer.
+ */
+static size_t
+fifo_ready(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
+{
+	size_t n;
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	n = CYAPA_BUFSIZE - (fifo->rindex & CYAPA_BUFMASK);
+	if (n > (size_t)(fifo->windex - fifo->rindex))
+		n = (size_t)(fifo->windex - fifo->rindex);
+	return (n);
+}
+
+/*
+ * Returns a read pointer into the fifo and then bumps
+ * rindex.  The FIFO must have at least 'n' characters in
+ * it.  The value (n) can cause the index to wrap but users
+ * of the buffer should never supply a value for (n) that wraps
+ * the buffer.
+ */
+static char *
+fifo_read(struct cyapa_softc *sc, struct cyapa_fifo *fifo, size_t n)
+{
+	char *ptr;
+
+	CYAPA_LOCK_ASSERT(sc);
+	if (n > (CYAPA_BUFSIZE - (fifo->rindex & CYAPA_BUFMASK))) {
+		printf("fifo_read: overflow\n");
+		return (fifo->buf);
+	}
+	ptr = fifo->buf + (fifo->rindex & CYAPA_BUFMASK);
+	fifo->rindex += n;
+
+	return (ptr);
+}
+
+static uint8_t
+fifo_read_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
+{
+	uint8_t c;
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	if (fifo->rindex == fifo->windex) {
+		printf("fifo_read_char: overflow\n");
+		c = 0;
+	} else {
+		c = fifo->buf[fifo->rindex & CYAPA_BUFMASK];
+		++fifo->rindex;
+	}
+	return (c);
+}
+
+
+/*
+ * Write a character to the FIFO.  The character will be discarded
+ * if the FIFO is full.
+ */
+static void
+fifo_write_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo, uint8_t c)
+{
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	if (fifo->windex - fifo->rindex < CYAPA_BUFSIZE) {
+		fifo->buf[fifo->windex & CYAPA_BUFMASK] = c;
+		++fifo->windex;
+	}
+}
+
+/*
+ * Return the amount of space available for writing without wrapping
+ * the fifo.
+ */
+static size_t
+fifo_space(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
+{
+	size_t n;
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	n = CYAPA_BUFSIZE - (fifo->windex & CYAPA_BUFMASK);
+	if (n > (size_t)(CYAPA_BUFSIZE - (fifo->windex - fifo->rindex)))
+		n = (size_t)(CYAPA_BUFSIZE - (fifo->windex - fifo->rindex));
+	return (n);
+}
+
+static char *
+fifo_write(struct cyapa_softc *sc, struct cyapa_fifo *fifo, size_t n)
+{
+	char *ptr;
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	ptr = fifo->buf + (fifo->windex & CYAPA_BUFMASK);
+	fifo->windex += n;
+
+	return (ptr);
+}
+
+static void
+fifo_reset(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
+{
+
+	CYAPA_LOCK_ASSERT(sc);
+
+	fifo->rindex = 0;
+	fifo->windex = 0;
+}
+
+/*
+ * Fuzz handling
+ */
+static int
+cyapa_fuzz(int delta, int *fuzzp)
+{
+	int fuzz;
+
+	fuzz = *fuzzp;
+	if (fuzz >= 0 && delta < 0) {
+		++delta;
+		--fuzz;
+	} else if (fuzz <= 0 && delta > 0) {
+		--delta;
+		++fuzz;
+	}
+	*fuzzp = fuzz;
+
+	return (delta);
+}
+
+DRIVER_MODULE(cyapa, smbus, cyapa_driver, cyapa_devclass, NULL, NULL);
+MODULE_DEPEND(cyapa, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
+MODULE_VERSION(cyapa, 1);

Property changes on: sys/dev/cyapa/cyapa.c
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: sys/dev/cyapa/cyapa.h
===================================================================
--- sys/dev/cyapa/cyapa.h	(revision 0)
+++ sys/dev/cyapa/cyapa.h	(working copy)
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_DEV_SMBUS_CYAPA_CYAPA_H_
+#define _SYS_DEV_SMBUS_CYAPA_CYAPA_H_
+
+#define CYAPA_MAX_MT    5
+
+/*
+ * Boot-time registers.  This is the device map
+ * if (stat & CYAPA_STAT_RUNNING) is 0.
+ */
+struct cyapa_boot_regs {
+	uint8_t stat;			/* CYAPA_STAT_xxx */
+	uint8_t boot;			/* CYAPA_BOOT_xxx */
+	uint8_t error;
+} __packed;
+
+#define CYAPA_BOOT_BUSY		0x80
+#define CYAPA_BOOT_RUNNING	0x10
+#define CYAPA_BOOT_DATA_VALID	0x08
+#define CYAPA_BOOT_CSUM_VALID	0x01
+
+#define CYAPA_ERROR_INVALID     0x80
+#define CYAPA_ERROR_INVALID_KEY 0x40
+#define CYAPA_ERROR_BOOTLOADER	0x20
+#define CYAPA_ERROR_CMD_CSUM    0x10
+#define CYAPA_ERROR_FLASH_PROT  0x08
+#define CYAPA_ERROR_FLASH_CSUM  0x04
+
+struct cyapa_regs {
+	uint8_t stat;
+	uint8_t fngr;
+
+	struct {
+		uint8_t xy_high;        /* 7:4 high 4 bits of x */
+		uint8_t x_low;          /* 3:0 high 4 bits of y */
+		uint8_t y_low;
+		uint8_t pressure;
+		uint8_t id;             /* 1-15 incremented each touch */
+	} touch[CYAPA_MAX_MT];
+} __packed;
+
+struct cyapa_cap {
+	uint8_t prod_ida[5];    /* 0x00 - 0x04 */
+	uint8_t prod_idb[6];    /* 0x05 - 0x0A */
+	uint8_t prod_idc[2];    /* 0x0B - 0x0C */
+	uint8_t reserved[6];    /* 0x0D - 0x12 */
+	uint8_t buttons;        /* 0x13 */
+	uint8_t gen;            /* 0x14, low 4 bits */
+	uint8_t max_abs_xy_high;/* 0x15 7:4 high x bits, 3:0 high y bits */
+	uint8_t max_abs_x_low;  /* 0x16 */
+	uint8_t max_abs_y_low;  /* 0x17 */
+	uint8_t phy_siz_xy_high;/* 0x18 7:4 high x bits, 3:0 high y bits */
+	uint8_t phy_siz_x_low;  /* 0x19 */
+	uint8_t phy_siz_y_low;  /* 0x1A */
+} __packed;
+
+#define CYAPA_STAT_RUNNING      0x80
+#define CYAPA_STAT_PWR_MASK     0x0C
+#define  CYAPA_PWR_OFF          0x00
+#define  CYAPA_PWR_IDLE         0x08
+#define  CYAPA_PWR_ACTIVE       0x0C
+
+#define CYAPA_STAT_DEV_MASK     0x03
+#define  CYAPA_DEV_NORMAL       0x03
+#define  CYAPA_DEV_BUSY         0x01
+
+#define CYAPA_FNGR_DATA_VALID   0x08
+#define CYAPA_FNGR_MIDDLE       0x04
+#define CYAPA_FNGR_RIGHT        0x02
+#define CYAPA_FNGR_LEFT         0x01
+#define CYAPA_FNGR_NUMFINGERS(c) (((c) >> 4) & 0x0F)
+
+#define CYAPA_TOUCH_X(regs, i)  ((((regs)->touch[i].xy_high << 4) & 0x0F00) | \
+				  (regs)->touch[i].x_low)
+#define CYAPA_TOUCH_Y(regs, i)  ((((regs)->touch[i].xy_high << 8) & 0x0F00) | \
+				  (regs)->touch[i].y_low)
+#define CYAPA_TOUCH_P(regs, i)  ((regs)->touch[i].pressure)
+
+#define CMD_BOOT_STATUS		0x00	/* only if in boot state */
+#define CMD_DEV_STATUS          0x00	/* only if in operational state */
+#define CMD_SOFT_RESET          0x28
+#define CMD_POWER_MODE          0x29
+#define  CMD_POWER_MODE_OFF	0x00
+#define  CMD_POWER_MODE_IDLE	0x14
+#define  CMD_POWER_MODE_FULL	0xFC
+#define CMD_QUERY_CAPABILITIES  0x2A
+
+#endif

Property changes on: sys/dev/cyapa/cyapa.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: sys/dev/ichiic/ig4_iic.c
===================================================================
--- sys/dev/ichiic/ig4_iic.c	(revision 0)
+++ sys/dev/ichiic/ig4_iic.c	(working copy)
@@ -0,0 +1,970 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Intel fourth generation mobile cpus integrated I2C device, smbus driver.
+ *
+ * See ig4_reg.h for datasheet reference and notes.
+ * See ig4_var.h for locking semantics.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/syslog.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/smbus/smbconf.h>
+
+#include <dev/ichiic/ig4_reg.h>
+#include <dev/ichiic/ig4_var.h>
+
+#define TRANS_NORMAL	1
+#define TRANS_PCALL	2
+#define TRANS_BLOCK	3
+
+static void ig4iic_start(void *xdev);
+static void ig4iic_intr(void *cookie);
+static void ig4iic_dump(ig4iic_softc_t *sc);
+
+static int ig4_dump;
+SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW,
+	   &ig4_dump, 0, "Dump controller registers");
+
+/*
+ * Low-level inline support functions
+ */
+static __inline void
+reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value)
+{
+	bus_write_4(sc->regs_res, reg, value);
+	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE);
+}
+
+static __inline uint32_t
+reg_read(ig4iic_softc_t *sc, uint32_t reg)
+{
+	uint32_t value;
+
+	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ);
+	value = bus_read_4(sc->regs_res, reg);
+	return (value);
+}
+
+/*
+ * Enable or disable the controller and wait for the controller to acknowledge
+ * the state change.
+ */
+static int
+set_controller(ig4iic_softc_t *sc, uint32_t ctl)
+{
+	int retry;
+	int error;
+	uint32_t v;
+
+	reg_write(sc, IG4_REG_I2C_EN, ctl);
+	error = SMB_ETIMEOUT;
+
+	for (retry = 100; retry > 0; --retry) {
+		v = reg_read(sc, IG4_REG_ENABLE_STATUS);
+		if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) {
+			error = 0;
+			break;
+		}
+		mtx_sleep(sc, &sc->io_lock, 0, "i2cslv", 1);
+	}
+	return (error);
+}
+
+/*
+ * Wait up to 25ms for the requested status using a 25uS polling loop.
+ */
+static int
+wait_status(ig4iic_softc_t *sc, uint32_t status)
+{
+	uint32_t v;
+	int error;
+	int txlvl = -1;
+	u_int count_us = 0;
+	u_int limit_us = 25000; /* 25ms */
+
+	error = SMB_ETIMEOUT;
+
+	for (;;) {
+		/*
+		 * Check requested status
+		 */
+		v = reg_read(sc, IG4_REG_I2C_STA);
+		if (v & status) {
+			error = 0;
+			break;
+		}
+
+		/*
+		 * When waiting for receive data break-out if the interrupt
+		 * loaded data into the FIFO.
+		 */
+		if (status & IG4_STATUS_RX_NOTEMPTY) {
+			if (sc->rpos != sc->rnext) {
+				error = 0;
+				break;
+			}
+		}
+
+		/*
+		 * When waiting for the transmit FIFO to become empty,
+		 * reset the timeout if we see a change in the transmit
+		 * FIFO level as progress is being made.
+		 */
+		if (status & IG4_STATUS_TX_EMPTY) {
+			v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK;
+			if (txlvl != v) {
+				txlvl = v;
+				count_us = 0;
+			}
+		}
+
+		/*
+		 * Stop if we've run out of time.
+		 */
+		if (count_us >= limit_us)
+			break;
+
+		/*
+		 * When waiting for receive data let the interrupt do its
+		 * work, otherwise poll with the lock held.
+		 */
+		if (status & IG4_STATUS_RX_NOTEMPTY) {
+			mtx_sleep(sc, &sc->io_lock, 0, "i2cwait",
+				  (hz + 99) / 100); /* sleep up to 10ms */
+			count_us += 10000;
+		} else {
+			DELAY(25);
+			count_us += 25;
+		}
+	}
+
+	return (error);
+}
+
+/*
+ * Read I2C data.  The data might have already been read by
+ * the interrupt code, otherwise it is sitting in the data
+ * register.
+ */
+static uint8_t
+data_read(ig4iic_softc_t *sc)
+{
+	uint8_t c;
+
+	if (sc->rpos == sc->rnext) {
+		c = (uint8_t)reg_read(sc, IG4_REG_DATA_CMD);
+	} else {
+		c = sc->rbuf[sc->rpos & IG4_RBUFMASK];
+		++sc->rpos;
+	}
+	return (c);
+}
+
+/*
+ * Set the slave address.  The controller must be disabled when
+ * changing the address.
+ *
+ * This operation does not issue anything to the I2C bus but sets
+ * the target address for when the controller later issues a START.
+ */
+static void
+set_slave_addr(ig4iic_softc_t *sc, uint8_t slave, int trans_op)
+{
+	uint32_t tar;
+	uint32_t ctl;
+	int use_10bit;
+
+	use_10bit = sc->use_10bit;
+	if (trans_op & SMB_TRANS_7BIT)
+		use_10bit = 0;
+	if (trans_op & SMB_TRANS_10BIT)
+		use_10bit = 1;
+
+	if (sc->slave_valid && sc->last_slave == slave &&
+	    sc->use_10bit == use_10bit) {
+		return;
+	}
+	sc->use_10bit = use_10bit;
+
+	/*
+	 * Wait for TXFIFO to drain before disabling the controller.
+	 *
+	 * If a write message has not been completed it's really a
+	 * programming error, but for now in that case issue an extra
+	 * byte + STOP.
+	 *
+	 * If a read message has not been completed it's also a programming
+	 * error, for now just ignore it.
+	 */
+	wait_status(sc, IG4_STATUS_TX_NOTFULL);
+	if (sc->write_started) {
+		reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_STOP);
+		sc->write_started = 0;
+	}
+	if (sc->read_started)
+		sc->read_started = 0;
+	wait_status(sc, IG4_STATUS_TX_EMPTY);
+
+	set_controller(sc, 0);
+	ctl = reg_read(sc, IG4_REG_CTL);
+	ctl &= ~IG4_CTL_10BIT;
+	ctl |= IG4_CTL_RESTARTEN;
+
+	tar = slave;
+	if (sc->use_10bit) {
+		tar |= IG4_TAR_10BIT;
+		ctl |= IG4_CTL_10BIT;
+	}
+	reg_write(sc, IG4_REG_CTL, ctl);
+	reg_write(sc, IG4_REG_TAR_ADD, tar);
+	set_controller(sc, IG4_I2C_ENABLE);
+	sc->slave_valid = 1;
+	sc->last_slave = slave;
+}
+
+/*
+ * Issue START with byte command, possible count, and a variable length
+ * read or write buffer, then possible turn-around read.  The read also
+ * has a possible count received.
+ *
+ * For SMBUS -
+ *
+ * Quick:		START+ADDR+RD/WR STOP
+ *
+ * Normal:		START+ADDR+WR CMD DATA..DATA STOP
+ *
+ *			START+ADDR+RD CMD
+ *			RESTART+ADDR RDATA..RDATA STOP
+ *			(can also be used for I2C transactions)
+ *
+ * Process Call:	START+ADDR+WR CMD DATAL DATAH
+ *			RESTART+ADDR+RD RDATAL RDATAH STOP
+ *
+ * Block:		START+ADDR+RD CMD
+ *			RESTART+ADDR+RD RCOUNT DATA... STOP
+ *
+ * 			START+ADDR+WR CMD
+ *			RESTART+ADDR+WR WCOUNT DATA... STOP
+ *
+ * For I2C - basically, no *COUNT fields, possibly no *CMD field.  If the
+ *	     sender needs to issue a 2-byte command it will incorporate it
+ *	     into the write buffer and also set NOCMD.
+ *
+ * Generally speaking, the START+ADDR / RESTART+ADDR is handled automatically
+ * by the controller at the beginning of a command sequence or on a data
+ * direction turn-around, and we only need to tell it when to issue the STOP.
+ */
+static int
+smb_transaction(ig4iic_softc_t *sc, char cmd, int op,
+		char *wbuf, int wcount, char *rbuf, int rcount, int *actualp)
+{
+	int error;
+	int unit;
+	uint32_t last;
+
+	/*
+	 * Debugging - dump registers
+	 */
+	if (ig4_dump) {
+		unit = device_get_unit(sc->dev);
+		if (ig4_dump & (1 << unit)) {
+			ig4_dump &= ~(1 << unit);
+			ig4iic_dump(sc);
+		}
+	}
+
+	/*
+	 * Issue START or RESTART with next data byte, clear any previous
+	 * abort condition that may have been holding the txfifo in reset.
+	 */
+	last = IG4_DATA_RESTART;
+	reg_read(sc, IG4_REG_CLR_TX_ABORT);
+	if (actualp)
+		*actualp = 0;
+
+	/*
+	 * Issue command if not told otherwise (smbus).
+	 */
+	if ((op & SMB_TRANS_NOCMD) == 0) {
+		error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+		if (error)
+			goto done;
+		last |= (u_char)cmd;
+		if (wcount == 0 && rcount == 0 && (op & SMB_TRANS_NOSTOP) == 0)
+			last |= IG4_DATA_STOP;
+		reg_write(sc, IG4_REG_DATA_CMD, last);
+		last = 0;
+	}
+
+	/*
+	 * Clean out any previously received data.
+	 */
+	if (sc->rpos != sc->rnext &&
+	    (op & SMB_TRANS_NOREPORT) == 0) {
+		device_printf(sc->dev,
+			      "discarding %d bytes of spurious data\n",
+			      sc->rnext - sc->rpos);
+	}
+	sc->rpos = 0;
+	sc->rnext = 0;
+
+	/*
+	 * If writing and not told otherwise, issue the write count (smbus).
+	 */
+	if (wcount && (op & SMB_TRANS_NOCNT) == 0) {
+		error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+		if (error)
+			goto done;
+		last |= (u_char)cmd;
+		reg_write(sc, IG4_REG_DATA_CMD, last);
+		last = 0;
+	}
+
+	/*
+	 * Bulk write (i2c)
+	 */
+	while (wcount) {
+		error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+		if (error)
+			goto done;
+		last |= (u_char)*wbuf;
+		if (wcount == 1 && rcount == 0 && (op & SMB_TRANS_NOSTOP) == 0)
+			last |= IG4_DATA_STOP;
+		reg_write(sc, IG4_REG_DATA_CMD, last);
+		--wcount;
+		++wbuf;
+		last = 0;
+	}
+
+	/*
+	 * Issue reads to xmit FIFO (strange, I know) to tell the controller
+	 * to clock in data.  At the moment just issue one read ahead to
+	 * pipeline the incoming data.
+	 *
+	 * NOTE: In the case of NOCMD and wcount == 0 we still issue a
+	 *	 RESTART here, even if the data direction has not changed
+	 *	 from the previous CHAINing call.  This we force the RESTART.
+	 *	 (A new START is issued automatically by the controller in
+	 *	 the other nominal cases such as a data direction change or
+	 *	 a previous STOP was issued).
+	 *
+	 * If this will be the last byte read we must also issue the STOP
+	 * at the end of the read.
+	 */
+	if (rcount) {
+		last = IG4_DATA_RESTART | IG4_DATA_COMMAND_RD;
+		if (rcount == 1 &&
+		    (op & (SMB_TRANS_NOSTOP | SMB_TRANS_NOCNT)) ==
+		    SMB_TRANS_NOCNT) {
+			last |= IG4_DATA_STOP;
+		}
+		reg_write(sc, IG4_REG_DATA_CMD, last);
+		last = IG4_DATA_COMMAND_RD;
+	}
+
+	/*
+	 * Bulk read (i2c) and count field handling (smbus)
+	 */
+	while (rcount) {
+		/*
+		 * Maintain a pipeline by queueing the allowance for the next
+		 * read before waiting for the current read.
+		 */
+		if (rcount > 1) {
+			if (op & SMB_TRANS_NOCNT)
+				last = (rcount == 2) ? IG4_DATA_STOP : 0;
+			else
+				last = 0;
+			reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_COMMAND_RD |
+							last);
+		}
+		error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY);
+		if (error) {
+			if ((op & SMB_TRANS_NOREPORT) == 0) {
+				device_printf(sc->dev,
+					      "rx timeout addr 0x%02x\n",
+					      sc->last_slave);
+			}
+			goto done;
+		}
+		last = data_read(sc);
+
+		if (op & SMB_TRANS_NOCNT) {
+			*rbuf = (u_char)last;
+			++rbuf;
+			--rcount;
+			if (actualp)
+				++*actualp;
+		} else {
+			/*
+			 * Handle count field (smbus), which is not part of
+			 * the rcount'ed buffer.  The first read data in a
+			 * bulk transfer is the count.
+			 *
+			 * XXX if rcount is loaded as 0 how do I generate a
+			 *     STOP now without issuing another RD or WR?
+			 */
+			if (rcount > (u_char)last)
+				rcount = (u_char)last;
+			op |= SMB_TRANS_NOCNT;
+		}
+	}
+	error = 0;
+done:
+	/* XXX wait for xmit buffer to become empty */
+	last = reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
+
+	return (error);
+}
+
+/*
+ *				SMBUS API FUNCTIONS
+ *
+ * Called from ig4iic_pci_attach/detach()
+ */
+int
+ig4iic_attach(ig4iic_softc_t *sc)
+{
+	int error;
+	uint32_t v;
+
+	v = reg_read(sc, IG4_REG_COMP_TYPE);
+	v = reg_read(sc, IG4_REG_COMP_PARAM1);
+	v = reg_read(sc, IG4_REG_GENERAL);
+	if ((v & IG4_GENERAL_SWMODE) == 0) {
+		v |= IG4_GENERAL_SWMODE;
+		reg_write(sc, IG4_REG_GENERAL, v);
+		v = reg_read(sc, IG4_REG_GENERAL);
+	}
+
+	v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
+	v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
+
+	v = reg_read(sc, IG4_REG_COMP_VER);
+	if (v != IG4_COMP_VER) {
+		error = ENXIO;
+		goto done;
+	}
+	v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+	v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+	v = reg_read(sc, IG4_REG_FS_SCL_HCNT);
+	v = reg_read(sc, IG4_REG_FS_SCL_LCNT);
+	v = reg_read(sc, IG4_REG_SDA_HOLD);
+
+	v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+	reg_write(sc, IG4_REG_FS_SCL_HCNT, v);
+	v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+	reg_write(sc, IG4_REG_FS_SCL_LCNT, v);
+
+	/*
+	 * Program based on a 25000 Hz clock.  This is a bit of a
+	 * hack (obviously).  The defaults are 400 and 470 for standard
+	 * and 60 and 130 for fast.  The defaults for standard fail
+	 * utterly (presumably cause an abort) because the clock time
+	 * is ~18.8ms by default.  This brings it down to ~4ms (for now).
+	 */
+	reg_write(sc, IG4_REG_SS_SCL_HCNT, 100);
+	reg_write(sc, IG4_REG_SS_SCL_LCNT, 125);
+	reg_write(sc, IG4_REG_FS_SCL_HCNT, 100);
+	reg_write(sc, IG4_REG_FS_SCL_LCNT, 125);
+
+	/*
+	 * Use a threshold of 1 so we get interrupted on each character,
+	 * allowing us to use mtx_sleep() in our poll code.  Not perfect
+	 * but this is better than using DELAY() for receiving data.
+	 *
+	 * See ig4_var.h for details on interrupt handler synchronization.
+	 */
+	reg_write(sc, IG4_REG_RX_TL, 1);
+
+	reg_write(sc, IG4_REG_CTL,
+		  IG4_CTL_MASTER |
+		  IG4_CTL_SLAVE_DISABLE |
+		  IG4_CTL_RESTARTEN |
+		  IG4_CTL_SPEED_STD);
+
+	sc->smb = device_add_child(sc->dev, "smbus", -1);
+	if (sc->smb == NULL) {
+		device_printf(sc->dev, "smbus driver not found\n");
+		error = ENXIO;
+		goto done;
+	}
+
+#if 0
+	/*
+	 * Don't do this, it blows up the PCI config
+	 */
+	reg_write(sc, IG4_REG_RESETS, IG4_RESETS_ASSERT);
+	reg_write(sc, IG4_REG_RESETS, IG4_RESETS_DEASSERT);
+#endif
+
+	/*
+	 * Interrupt on STOP detect or receive character ready
+	 */
+	reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET |
+					 IG4_INTR_RX_FULL);
+	mtx_lock(&sc->io_lock);
+	if (set_controller(sc, 0))
+		device_printf(sc->dev, "controller error during attach-1\n");
+	if (set_controller(sc, IG4_I2C_ENABLE))
+		device_printf(sc->dev, "controller error during attach-2\n");
+	mtx_unlock(&sc->io_lock);
+	error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
+			       NULL, ig4iic_intr, sc, &sc->intr_handle);
+	if (error) {
+		device_printf(sc->dev,
+			      "Unable to setup irq: error %d\n", error);
+	}
+
+	sc->enum_hook.ich_func = ig4iic_start;
+	sc->enum_hook.ich_arg = sc->dev;
+
+	/* We have to wait until interrupts are enabled. I2C read and write
+	 * only works if the interrupts are available.
+	 */
+	if (config_intrhook_establish(&sc->enum_hook) != 0)
+		error = ENOMEM;
+	else
+		error = 0;
+
+done:
+	return (error);
+}
+
+void
+ig4iic_start(void *xdev)
+{
+	int error;
+	ig4iic_softc_t *sc;
+	device_t dev = (device_t)xdev;
+
+	sc = device_get_softc(dev);
+
+	config_intrhook_disestablish(&sc->enum_hook);
+
+	/* Attach us to the smbus */
+	error = bus_generic_attach(sc->dev);
+	if (error) {
+		device_printf(sc->dev,
+			      "failed to attach child: error %d\n", error);
+	}
+}
+
+
+
+int
+ig4iic_detach(ig4iic_softc_t *sc)
+{
+	int error;
+
+	if (device_is_attached(sc->dev)) {
+		error = bus_generic_detach(sc->dev);
+		if (error)
+			return (error);
+	}
+	if (sc->smb)
+		device_delete_child(sc->dev, sc->smb);
+	if (sc->intr_handle)
+		bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	sc->smb = NULL;
+	sc->intr_handle = NULL;
+	reg_write(sc, IG4_REG_INTR_MASK, 0);
+	reg_read(sc, IG4_REG_CLR_INTR);
+	set_controller(sc, 0);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (0);
+}
+
+int
+ig4iic_smb_callback(device_t dev, int index, void *data)
+{
+	int error;
+
+	switch (index) {
+	case SMB_REQUEST_BUS:
+		error = 0;
+		break;
+	case SMB_RELEASE_BUS:
+		error = 0;
+		break;
+	default:
+		error = SMB_EABORT;
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * Quick command.  i.e. START + cmd + R/W + STOP and no data.  It is
+ * unclear to me how I could implement this with the intel i2c controller
+ * because the controler sends STARTs and STOPs automatically with data.
+ */
+int
+ig4iic_smb_quick(device_t dev, u_char slave, int how)
+{
+
+	return (SMB_ENOTSUPP);
+}
+
+/*
+ * Incremental send byte without stop (?).  It is unclear why the slave
+ * address is specified if this presumably is used in combination with
+ * ig4iic_smb_quick().
+ *
+ * (Also, how would this work anyway?  Issue the last byte with writeb()?)
+ */
+int
+ig4iic_smb_sendb(device_t dev, u_char slave, char byte)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	uint32_t cmd;
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	cmd = byte;
+	if (wait_status(sc, IG4_STATUS_TX_NOTFULL) == 0) {
+		reg_write(sc, IG4_REG_DATA_CMD, cmd);
+		error = 0;
+	} else {
+		error = SMB_ETIMEOUT;
+	}
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * Incremental receive byte without stop (?).  It is unclear why the slave
+ * address is specified if this presumably is used in combination with
+ * ig4iic_smb_quick().
+ */
+int
+ig4iic_smb_recvb(device_t dev, u_char slave, char *byte)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_COMMAND_RD);
+	if (wait_status(sc, IG4_STATUS_RX_NOTEMPTY) == 0) {
+		*byte = data_read(sc);
+		error = 0;
+	} else {
+		*byte = 0;
+		error = SMB_ETIMEOUT;
+	}
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * Write command and single byte in transaction.
+ */
+int
+ig4iic_smb_writeb(device_t dev, u_char slave, char cmd, char byte)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+				&byte, 1, NULL, 0, NULL);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * Write command and single word in transaction.
+ */
+int
+ig4iic_smb_writew(device_t dev, u_char slave, char cmd, short word)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	char buf[2];
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	buf[0] = word & 0xFF;
+	buf[1] = word >> 8;
+	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+				buf, 2, NULL, 0, NULL);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * write command and read single byte in transaction.
+ */
+int
+ig4iic_smb_readb(device_t dev, u_char slave, char cmd, char *byte)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+				NULL, 0, byte, 1, NULL);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * write command and read word in transaction.
+ */
+int
+ig4iic_smb_readw(device_t dev, u_char slave, char cmd, short *word)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	char buf[2];
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	if ((error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+				     NULL, 0, buf, 2, NULL)) == 0) {
+		*word = (u_char)buf[0] | ((u_char)buf[1] << 8);
+	}
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * write command and word and read word in transaction
+ */
+int
+ig4iic_smb_pcall(device_t dev, u_char slave, char cmd,
+		 short sdata, short *rdata)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	char rbuf[2];
+	char wbuf[2];
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	wbuf[0] = sdata & 0xFF;
+	wbuf[1] = sdata >> 8;
+	if ((error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+				     wbuf, 2, rbuf, 2, NULL)) == 0) {
+		*rdata = (u_char)rbuf[0] | ((u_char)rbuf[1] << 8);
+	}
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+int
+ig4iic_smb_bwrite(device_t dev, u_char slave, char cmd,
+		  u_char wcount, char *buf)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	error = smb_transaction(sc, cmd, 0,
+				buf, wcount, NULL, 0, NULL);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+int
+ig4iic_smb_bread(device_t dev, u_char slave, char cmd,
+		 u_char *countp_char, char *buf)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int rcount = *countp_char;
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, 0);
+	error = smb_transaction(sc, cmd, 0,
+				NULL, 0, buf, rcount, &rcount);
+	*countp_char = rcount;
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+int
+ig4iic_smb_trans(device_t dev, int slave, char cmd, int op,
+		 char *wbuf, int wcount, char *rbuf, int rcount,
+		 int *actualp)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
+
+	set_slave_addr(sc, slave, op);
+	error = smb_transaction(sc, cmd, op,
+				wbuf, wcount, rbuf, rcount, actualp);
+
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
+	return (error);
+}
+
+/*
+ * Interrupt Operation, see ig4_var.h for locking semantics.
+ */
+static void
+ig4iic_intr(void *cookie)
+{
+	ig4iic_softc_t *sc = cookie;
+	uint32_t status;
+
+	mtx_lock(&sc->io_lock);
+/*	reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/
+	status = reg_read(sc, IG4_REG_I2C_STA);
+	while (status & IG4_STATUS_RX_NOTEMPTY) {
+		sc->rbuf[sc->rnext & IG4_RBUFMASK] =
+		    (uint8_t)reg_read(sc, IG4_REG_DATA_CMD);
+		++sc->rnext;
+		status = reg_read(sc, IG4_REG_I2C_STA);
+	}
+	reg_read(sc, IG4_REG_CLR_INTR);
+	wakeup(sc);
+	mtx_unlock(&sc->io_lock);
+}
+
+#define REGDUMP(sc, reg)	\
+	device_printf(sc->dev, "  %-23s %08x\n", #reg, reg_read(sc, reg))
+
+static void
+ig4iic_dump(ig4iic_softc_t *sc)
+{
+	device_printf(sc->dev, "ig4iic register dump:\n");
+	REGDUMP(sc, IG4_REG_CTL);
+	REGDUMP(sc, IG4_REG_TAR_ADD);
+	REGDUMP(sc, IG4_REG_SS_SCL_HCNT);
+	REGDUMP(sc, IG4_REG_SS_SCL_LCNT);
+	REGDUMP(sc, IG4_REG_FS_SCL_HCNT);
+	REGDUMP(sc, IG4_REG_FS_SCL_LCNT);
+	REGDUMP(sc, IG4_REG_INTR_STAT);
+	REGDUMP(sc, IG4_REG_INTR_MASK);
+	REGDUMP(sc, IG4_REG_RAW_INTR_STAT);
+	REGDUMP(sc, IG4_REG_RX_TL);
+	REGDUMP(sc, IG4_REG_TX_TL);
+	REGDUMP(sc, IG4_REG_I2C_EN);
+	REGDUMP(sc, IG4_REG_I2C_STA);
+	REGDUMP(sc, IG4_REG_TXFLR);
+	REGDUMP(sc, IG4_REG_RXFLR);
+	REGDUMP(sc, IG4_REG_SDA_HOLD);
+	REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE);
+	REGDUMP(sc, IG4_REG_SLV_DATA_NACK);
+	REGDUMP(sc, IG4_REG_DMA_CTRL);
+	REGDUMP(sc, IG4_REG_DMA_TDLR);
+	REGDUMP(sc, IG4_REG_DMA_RDLR);
+	REGDUMP(sc, IG4_REG_SDA_SETUP);
+	REGDUMP(sc, IG4_REG_ENABLE_STATUS);
+	REGDUMP(sc, IG4_REG_COMP_PARAM1);
+	REGDUMP(sc, IG4_REG_COMP_VER);
+	REGDUMP(sc, IG4_REG_COMP_TYPE);
+	REGDUMP(sc, IG4_REG_CLK_PARMS);
+	REGDUMP(sc, IG4_REG_RESETS);
+	REGDUMP(sc, IG4_REG_GENERAL);
+	REGDUMP(sc, IG4_REG_SW_LTR_VALUE);
+	REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE);
+}
+#undef REGDUMP
+
+DRIVER_MODULE(smbus, ig4iic, smbus_driver, smbus_devclass, NULL, NULL);

Property changes on: sys/dev/ichiic/ig4_iic.c
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: sys/dev/ichiic/ig4_pci.c
===================================================================
--- sys/dev/ichiic/ig4_pci.c	(revision 0)
+++ sys/dev/ichiic/ig4_pci.c	(working copy)
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Intel fourth generation mobile cpus integrated I2C device, smbus driver.
+ *
+ * See ig4_reg.h for datasheet reference and notes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/syslog.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/smbus/smbconf.h>
+
+#include "smbus_if.h"
+
+#include <dev/ichiic/ig4_reg.h>
+#include <dev/ichiic/ig4_var.h>
+
+static int ig4iic_pci_detach(device_t dev);
+
+#define PCI_CHIP_LYNXPT_LP_I2C_1	0x9c618086
+#define PCI_CHIP_LYNXPT_LP_I2C_2	0x9c628086
+
+static int
+ig4iic_pci_probe(device_t dev)
+{
+	switch(pci_get_devid(dev)) {
+	case PCI_CHIP_LYNXPT_LP_I2C_1:
+		device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1");
+		break;
+	case PCI_CHIP_LYNXPT_LP_I2C_2:
+		device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2");
+		break;
+	default:
+		return (ENXIO);
+	}
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ig4iic_pci_attach(device_t dev)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	bzero(sc, sizeof(*sc));
+
+	mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
+	sx_init(&sc->call_lock, "IG4 call lock");
+
+	sc->dev = dev;
+	sc->regs_rid = PCIR_BAR(0);
+	sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+					  &sc->regs_rid, RF_ACTIVE);
+	if (sc->regs_res == NULL) {
+		device_printf(dev, "unable to map registers\n");
+		ig4iic_pci_detach(dev);
+		return (ENXIO);
+	}
+	sc->intr_rid = 0;
+	if (pci_alloc_msi(dev, &sc->intr_rid)) {
+		device_printf(dev, "Using MSI\n");
+	}
+	sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+					  &sc->intr_rid, RF_SHAREABLE | RF_ACTIVE);
+	if (sc->intr_res == NULL) {
+		device_printf(dev, "unable to map interrupt\n");
+		ig4iic_pci_detach(dev);
+		return (ENXIO);
+	}
+	sc->pci_attached = 1;
+
+	error = ig4iic_attach(sc);
+	if (error)
+		ig4iic_pci_detach(dev);
+
+	return (error);
+}
+
+static int
+ig4iic_pci_detach(device_t dev)
+{
+	ig4iic_softc_t *sc = device_get_softc(dev);
+	int error;
+
+	if (sc->pci_attached) {
+		error = ig4iic_detach(sc);
+		if (error)
+			return (error);
+		sc->pci_attached = 0;
+	}
+
+	if (sc->intr_res) {
+		bus_release_resource(dev, SYS_RES_IRQ,
+				     sc->intr_rid, sc->intr_res);
+		sc->intr_res = NULL;
+	}
+	if (sc->intr_rid != 0)
+		pci_release_msi(dev);
+	if (sc->regs_res) {
+		bus_release_resource(dev, SYS_RES_MEMORY,
+				     sc->regs_rid, sc->regs_res);
+		sc->regs_res = NULL;
+	}
+	if (mtx_initialized(&sc->io_lock)) {
+		mtx_destroy(&sc->io_lock);
+		sx_destroy(&sc->call_lock);
+	}
+
+	return (0);
+}
+
+static device_method_t ig4iic_pci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, ig4iic_pci_probe),
+	DEVMETHOD(device_attach, ig4iic_pci_attach),
+	DEVMETHOD(device_detach, ig4iic_pci_detach),
+
+	/* SMBus methods from ig4_smb.c */
+	DEVMETHOD(smbus_callback, ig4iic_smb_callback),
+	DEVMETHOD(smbus_quick, ig4iic_smb_quick),
+	DEVMETHOD(smbus_sendb, ig4iic_smb_sendb),
+	DEVMETHOD(smbus_recvb, ig4iic_smb_recvb),
+	DEVMETHOD(smbus_writeb, ig4iic_smb_writeb),
+	DEVMETHOD(smbus_writew, ig4iic_smb_writew),
+	DEVMETHOD(smbus_readb, ig4iic_smb_readb),
+	DEVMETHOD(smbus_readw, ig4iic_smb_readw),
+	DEVMETHOD(smbus_pcall, ig4iic_smb_pcall),
+	DEVMETHOD(smbus_bwrite, ig4iic_smb_bwrite),
+	DEVMETHOD(smbus_bread, ig4iic_smb_bread),
+	DEVMETHOD(smbus_trans, ig4iic_smb_trans),
+
+	DEVMETHOD_END
+};
+
+static driver_t ig4iic_pci_driver = {
+	"ig4iic",
+	ig4iic_pci_methods,
+	sizeof(struct ig4iic_softc)
+};
+
+static devclass_t ig4iic_pci_devclass;
+
+DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0);
+MODULE_DEPEND(ig4iic, pci, 1, 1, 1);
+MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
+MODULE_VERSION(ig4iic, 1);

Property changes on: sys/dev/ichiic/ig4_pci.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: sys/dev/ichiic/ig4_reg.h
===================================================================
--- sys/dev/ichiic/ig4_reg.h	(revision 0)
+++ sys/dev/ichiic/ig4_reg.h	(working copy)
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Intel fourth generation mobile cpus integrated I2C device.
+ *
+ * Datasheet reference:  Section 22.
+ *
+ * http://www.intel.com/content/www/us/en/processors/core/4th-gen-core-family-mobile-i-o-datasheet.html?wapkw=datasheets+4th+generation
+ *
+ * This is a from-scratch driver under the BSD license using the Intel data
+ * sheet and the linux driver for reference.  All code is freshly written
+ * without referencing the linux driver code.  However, during testing
+ * I am also using the linux driver code as a reference to help resolve any
+ * issues that come.  These will be specifically documented in the code.
+ *
+ * Please see protocol notes in section 5.21.  This controller is an I2C
+ * master only and cannot act as a slave.  The IO voltage should be set by
+ * the BIOS.  Standard (100Kb/s) and Fast (400Kb/s) and fast mode plus
+ * (1MB/s) is supported.  High speed mode (3.4 MB/s) is NOT supported.
+ */
+
+#ifndef _BUS_SMBUS_INTELGEN4_IG4_REG_H_
+#define _BUS_SMBUS_INTELGEN4_IG4_REG_H_
+
+/*
+ * 22.2 MMIO registers can be accessed through BAR0 in PCI mode or through
+ *	BAR1 when in ACPI mode.
+ *
+ *	Register width is 32-bits
+ *
+ * 22.2 Default Values on device reset are 0 except as specified here:
+ *	TAR_ADD		0x00000055
+ *	SS_SCL_HCNT	0x00000264
+ *	SS_SCL_LCNT	0x000002C2
+ *	FS_SCL_HCNT	0x0000006E
+ *	FS_SCL_LCNT	0x000000CF
+ *	INTR_MASK	0x000008FF
+ *	I2C_STA		0x00000006
+ *	SDA_HOLD	0x00000001
+ *	SDA_SETUP	0x00000064
+ *	COMP_PARAM1	0x00FFFF6E
+ *	COMP_VER	0x3131352A
+ */
+
+#define IG4_REG_CTL		0x0000	/* RW	Control Register */
+#define IG4_REG_TAR_ADD		0x0004	/* RW	Target Address */
+#define IG4_REG_DATA_CMD	0x0010	/* RW	Data Buffer and Command */
+#define IG4_REG_SS_SCL_HCNT	0x0014	/* RW	Std Speed clock High Count */
+#define IG4_REG_SS_SCL_LCNT	0x0018	/* RW	Std Speed clock Low Count */
+#define IG4_REG_FS_SCL_HCNT	0x001C	/* RW	Fast Speed clock High Count */
+#define IG4_REG_FS_SCL_LCNT	0x0020	/* RW	Fast Speed clock Low Count */
+#define IG4_REG_INTR_STAT	0x002C	/* RO	Interrupt Status */
+#define IG4_REG_INTR_MASK	0x0030	/* RW	Interrupt Mask */
+#define IG4_REG_RAW_INTR_STAT	0x0034	/* RO	Raw Interrupt Status */
+#define IG4_REG_RX_TL		0x0038	/* RW	Receive FIFO Threshold */
+#define IG4_REG_TX_TL		0x003C	/* RW	Transmit FIFO Threshold */
+#define IG4_REG_CLR_INTR	0x0040	/* RO	Clear Interrupt */
+#define IG4_REG_CLR_RX_UNDER	0x0044	/* RO	Clear RX_Under Interrupt */
+#define IG4_REG_CLR_RX_OVER	0x0048	/* RO	Clear RX_Over Interrupt */
+#define IG4_REG_CLR_TX_OVER	0x004C	/* RO	Clear TX_Over Interrupt */
+#define IG4_REG_CLR_TX_ABORT	0x0054	/* RO	Clear TX_Abort Interrupt */
+#define IG4_REG_CLR_ACTIVITY	0x005C	/* RO	Clear Activity Interrupt */
+#define IG4_REG_CLR_STOP_DET	0x0060	/* RO	Clear STOP Detection Int */
+#define IG4_REG_CLR_START_DET	0x0064	/* RO	Clear START Detection Int */
+#define IG4_REG_CLR_GEN_CALL	0x0068	/* RO	Clear General Call Interrupt */
+#define IG4_REG_I2C_EN		0x006C	/* RW	I2C Enable */
+#define IG4_REG_I2C_STA		0x0070	/* RO	I2C Status */
+#define IG4_REG_TXFLR		0x0074	/* RO	Transmit FIFO Level */
+#define IG4_REG_RXFLR		0x0078	/* RO	Receive FIFO Level */
+#define IG4_REG_SDA_HOLD	0x007C	/* RW	SDA Hold Time Length */
+#define IG4_REG_TX_ABRT_SOURCE	0x0080	/* RO	Transmit Abort Source */
+#define IG4_REG_SLV_DATA_NACK	0x0084	/* RW	General Slave Data NACK */
+#define IG4_REG_DMA_CTRL	0x0088	/* RW	DMA Control */
+#define IG4_REG_DMA_TDLR	0x008C	/* RW	DMA Transmit Data Level */
+#define IG4_REG_DMA_RDLR	0x0090	/* RW	DMA Receive Data Level */
+#define IG4_REG_SDA_SETUP	0x0094	/* RW	SDA Setup */
+#define IG4_REG_ENABLE_STATUS	0x009C	/* RO	Enable Status */
+#define IG4_REG_COMP_PARAM1	0x00F4	/* RO	Component Parameter */
+#define IG4_REG_COMP_VER	0x00F8	/* RO	Component Version */
+#define IG4_REG_COMP_TYPE	0x00FC	/* RO	Probe width/endian? (linux) */
+#define IG4_REG_CLK_PARMS	0x0800	/* RW	Clock Parameters */
+#define IG4_REG_RESETS		0x0804	/* RW	Reset Register */
+#define IG4_REG_GENERAL		0x0808	/* RW	General Register */
+#define IG4_REG_SW_LTR_VALUE	0x0810	/* RW	SW LTR Value */
+#define IG4_REG_AUTO_LTR_VALUE	0x0814	/* RW	Auto LTR Value */
+
+/*
+ * CTL - Control Register 		22.2.1
+ *	 Default Value: 0x0000007F.
+ *
+ *	RESTARTEN	- RW Restart Enable
+ *	10BIT		- RW Controller operates in 10-bit mode, else 7-bit
+ *
+ * NOTE: When restart is disabled the controller is incapable of
+ *	 performing the following functions:
+ *
+ *		 Sending a START Byte
+ *		 Performing any high-speed mode op
+ *		 Performing direction changes in combined format mode
+ *		 Performing a read operation with a 10-bit address
+ *
+ *	 Attempting to perform the above operations will result in the
+ *	 TX_ABORT bit being set in RAW_INTR_STAT.
+ */
+#define IG4_CTL_SLAVE_DISABLE	0x0040	/* snarfed from linux */
+#define IG4_CTL_RESTARTEN	0x0020	/* Allow Restart when master */
+#define IG4_CTL_10BIT		0x0010	/* ctlr accepts 10-bit addresses */
+#define IG4_CTL_SPEED_FAST	0x0004	/* snarfed from linux */
+#define IG4_CTL_SPEED_STD	0x0002	/* snarfed from linux */
+#define IG4_CTL_MASTER		0x0001	/* snarfed from linux */
+
+/*
+ * TAR_ADD - Target Address Register	22.2.2
+ *	     Default Value: 0x00000055F
+ *
+ *	10BIT		- RW controller starts its transfers in 10-bit
+ *			  address mode, else 7-bit.
+ *
+ *	SPECIAL		- RW Indicates whether software performs a General Call
+ *			  or START BYTE command.
+ *
+ *		0	  Ignore GC_OR_START and use TAR address.
+ *
+ *		1	  Perform special I2C Command based on GC_OR_START.
+ *
+ *	GC_OR_START	- RW (only if SPECIAL is set)
+ *
+ *		0	  General Call Address.  After issuing a General Call,
+ *			  only writes may be performed.  Attempting to issue
+ *			  a read command results in IX_ABRT in RAW_INTR_STAT.
+ *			  The controller remains in General Call mode until
+ *			  bit 11 (SPECIAL) is cleared.
+ *
+ *		1	  START BYTE.
+ *
+ *
+ * 	IC_TAR		- RW when transmitting a general call, these bits are
+ *			  ignored.  To generate a START BYTE, the address
+ *			  needs to be written into these bits once.
+ *
+ * This register should only be updated when the IIC is disabled (I2C_ENABLE=0)
+ */
+#define IG4_TAR_10BIT		0x1000	/* start xfer in 10-bit mode */
+#define IG4_TAR_SPECIAL		0x0800	/* Perform special command */
+#define IG4_TAR_GC_OR_START	0x0400	/* General Call or Start */
+#define IG4_TAR_ADDR_MASK	0x03FF	/* Target address */
+
+/*
+ * TAR_DATA_CMD - Data Buffer and Command Register	22.2.3
+ *
+ *	RESTART		- RW This bit controls whether a forced RESTART is
+ *			  issued before the byte is sent or received.
+ *
+ *		0	  If not set a RESTART is only issued if the tranfer
+ *			  direction is changing from the previous command.
+ *
+ *		1	  A RESTART is issued before the byte is sent or
+ *			  received, regardless of whether or not the transfer
+ *			  direction is changing from the previous command.
+ *
+ *	STOP		- RW This bit controls whether a STOP is issued after
+ *			  the byte is sent or received.
+ *
+ *		0	  STOP is not issued after this byte, regardless
+ *			  of whether or not the Tx FIFO is empty.
+ *
+ *		1	  STOP is issued after this byte, regardless of
+ *			  whether or not the Tx FIFO is empty.  If the
+ *			  Tx FIFO is not empty the master immediately tries
+ *			  to start a new transfer by issuing a START and
+ *			  arbitrating for the bus.
+ *
+ *			  i.e. the STOP is issued along with this byte,
+ *			  within the write stream.
+ *
+ *	COMMAND		- RW Control whether a read or write is performed.
+ *
+ *		0	  WRITE
+ *
+ *		1	  READ
+ *
+ *	DATA (7:0)	- RW Contains the data to be transmitted or received
+ *			  on the I2C bus.
+ *
+ *	NOTE: Writing to this register causes a START + slave + RW to be
+ *	      issued if the direction has changed or the last data byte was
+ *	      sent with a STOP.
+ *
+ *	NOTE: We control termination?  so this register must be written
+ *	      for each byte we wish to receive.  We can then drain the
+ *	      receive FIFO.
+ */
+
+#define IG4_DATA_RESTART	0x0400	/* Force RESTART */
+#define IG4_DATA_STOP		0x0200	/* Force STOP[+START] */
+#define IG4_DATA_COMMAND_RD	0x0100	/* bus direction 0=write 1=read */
+#define IG4_DATA_MASK		0x00FF
+
+/*
+ * SS_SCL_HCNT - Standard Speed Clock High Count Register	22.2.4
+ * SS_SCL_LCNT - Standard Speed Clock Low Count Register	22.2.5
+ * FS_SCL_HCNT - Fast Speed Clock High Count Register		22.2.6
+ * FS_SCL_LCNT - Fast Speed Clock Low Count Register		22.2.7
+ *
+ *	COUNT (15:0)	- Set the period count to a value between 6 and
+ *			  65525.
+ */
+#define IG4_SCL_CLOCK_MASK	0xFFFFU	/* count bits in register */
+
+/*
+ * INTR_STAT	- (RO) Interrupt Status Register		22.2.8
+ * INTR_MASK	- (RW) Interrupt Mask Register			22.2.9
+ * RAW_INTR_STAT- (RO) Raw Interrupt Status Register		22.2.10
+ *
+ *	GEN_CALL	Set only when a general call (broadcast) address
+ *			is received and acknowleged, stays set until
+ *			cleared by reading CLR_GEN_CALL.
+ *
+ *	START_DET	Set when a START or RESTART condition has occurred
+ *			on the interface.
+ *
+ *	STOP_DET	Set when a STOP condition has occurred on the
+ *			interface.
+ *
+ *	ACTIVITY	Set by any activity on the interface.  Cleared
+ *			by reading CLR_ACTIVITY or CLR_INTR.
+ *
+ *	TX_ABRT		Indicates the controller as a transmitter is
+ *			unable to complete the intended action.  When set,
+ *			the controller will hold the TX FIFO in a reset
+ *			state (flushed) until CLR_TX_ABORT is read to
+ *			clear the condition.  Once cleared, the TX FIFO
+ *			will be available again.
+ *
+ *	TX_EMPTY	Indicates that the transmitter is at or below
+ *			the specified TX_TL threshold.  Automatically
+ *			cleared by HW when the buffer level goes above
+ *			the threshold.
+ *
+ *	TX_OVER		Indicates that the processer attempted to write
+ *			to the TX FIFO while the TX FIFO was full.  Cleared
+ *			by reading CLR_TX_OVER.
+ *
+ *	RX_FULL		Indicates that the receive FIFO has reached or
+ *			exceeded the specified RX_TL threshold.  Cleared
+ *			by HW when the cpu drains the FIFO to below the
+ *			threshold.
+ *
+ *	RX_OVER		Indicates that the receive FIFO was unable to
+ *			accept new data and data was lost.  Cleared by
+ *			reading CLR_RX_OVER.
+ *
+ *	RX_UNDER	Indicates that the cpu attempted to read data
+ *			from the receive buffer while the RX FIFO was
+ *			empty.  Cleared by reading CLR_RX_UNDER.
+ *
+ * NOTES ON RAW_INTR_STAT:
+ *
+ *	This register can be used to monitor the GEN_CALL, START_DET,
+ *	STOP_DET, ACTIVITY, TX_ABRT, TX_EMPTY, TX_OVER, RX_FULL, RX_OVER,
+ *	and RX_UNDER bits.  The documentation is a bit unclear but presumably
+ *	this is the unlatched version.
+ *
+ *	Code should test FIFO conditions using the I2C_STA (status) register,
+ *	not the interrupt status registers.
+ */
+
+#define IG4_INTR_GEN_CALL	0x0800
+#define IG4_INTR_START_DET	0x0400
+#define IG4_INTR_STOP_DET	0x0200
+#define IG4_INTR_ACTIVITY	0x0100
+#define IG4_INTR_TX_ABRT	0x0040
+#define IG4_INTR_TX_EMPTY	0x0010
+#define IG4_INTR_TX_OVER	0x0008
+#define IG4_INTR_RX_FULL	0x0004
+#define IG4_INTR_RX_OVER	0x0002
+#define IG4_INTR_RX_UNDER	0x0001
+
+/*
+ * RX_TL	- (RW) Receive FIFO Threshold Register		22.2.11
+ * TX_TL	- (RW) Transmit FIFO Threshold Register		22.2.12
+ *
+ * 	Specify the receive and transmit FIFO threshold register.  The
+ *	FIFOs have 16 elements.  The valid range is 0-15.  Setting a
+ *	value greater than 15 causes the actual value to be the maximum
+ *	depth of the FIFO.
+ *
+ *	Generally speaking since everything is messaged, we can use a
+ *	mid-level setting for both parameters and (e.g.) fully drain the
+ *	receive FIFO on the STOP_DET condition to handle loose ends.
+ */
+#define IG4_FIFO_MASK		0x00FF
+#define IG4_FIFO_LIMIT		16
+
+/*
+ * CLR_INTR	- (RO) Clear Interrupt Register			22.2.13
+ * CLR_RX_UNDER	- (RO) Clear Interrupt Register (specific)	22.2.14
+ * CLR_RX_OVER	- (RO) Clear Interrupt Register (specific)	22.2.15
+ * CLR_TX_OVER	- (RO) Clear Interrupt Register (specific)	22.2.16
+ * CLR_TX_ABORT	- (RO) Clear Interrupt Register (specific)	22.2.17
+ * CLR_ACTIVITY	- (RO) Clear Interrupt Register (specific)	22.2.18
+ * CLR_STOP_DET	- (RO) Clear Interrupt Register (specific)	22.2.19
+ * CLR_START_DET- (RO) Clear Interrupt Register (specific)	22.2.20
+ * CLR_GEN_CALL	- (RO) Clear Interrupt Register (specific)	22.2.21
+ *
+ *	CLR_* specific operations clear the appropriate bit in the
+ *	RAW_INTR_STAT register.  Intel does not really document whether
+ *	these operations clear the normal interrupt status register.
+ *
+ *	CLR_INTR clears bits in the normal interrupt status register and
+ *	presumably also the raw(?) register?  Intel is again unclear.
+ *
+ * NOTE: CLR_INTR only clears software-clearable interrupts.  Hardware
+ *	 clearable interrupts are controlled entirely by the hardware.
+ *	 CLR_INTR also clears the TX_ABRT_SOURCE register.
+ *
+ * NOTE: CLR_TX_ABORT also clears the TX_ABRT_SOURCE register and releases
+ *	 the TX FIFO from its flushed/reset state, allowing more writes
+ *	 to the TX FIFO.
+ *
+ * NOTE: CLR_ACTIVITY has no effect if the I2C bus is still active.
+ *	 Intel documents that the bit is automatically cleared when
+ *	 there is no further activity on the bus.
+ */
+#define IG4_CLR_BIT		0x0001		/* Reflects source */
+
+/*
+ * I2C_EN	- (RW) I2C Enable Register			22.2.22
+ *
+ *	ABORT		Software can abort an I2C transfer by setting this
+ *			bit.  Hardware will clear the bit once the STOP has
+ *			been detected.  This bit can only be set while the
+ *			I2C interface is enabled.
+ *
+ *	I2C_ENABLE	Enable the controller, else disable it.
+ *			(Use I2C_ENABLE_STATUS to poll enable status
+ *			& wait for changes)
+ */
+#define IG4_I2C_ABORT		0x0002
+#define IG4_I2C_ENABLE		0x0001
+
+/*
+ * I2C_STA	- (RO) I2C Status Register			22.2.23
+ */
+#define IG4_STATUS_ACTIVITY	0x0020	/* Controller is active */
+#define IG4_STATUS_RX_FULL	0x0010	/* RX FIFO completely full */
+#define IG4_STATUS_RX_NOTEMPTY	0x0008	/* RX FIFO not empty */
+#define IG4_STATUS_TX_EMPTY	0x0004	/* TX FIFO completely empty */
+#define IG4_STATUS_TX_NOTFULL	0x0002	/* TX FIFO not full */
+#define IG4_STATUS_I2C_ACTIVE	0x0001	/* I2C bus is active */
+
+/*
+ * TXFLR	- (RO) Transmit FIFO Level Register		22.2.24
+ * RXFLR	- (RO) Receive FIFO Level Register		22.2.25
+ *
+ *	Read the number of entries currently in the Transmit or Receive
+ *	FIFOs.  Note that for some reason the mask is 9 bits instead of
+ *	the 8 bits the fill level controls.
+ */
+#define IG4_FIFOLVL_MASK	0x001F
+
+/*
+ * SDA_HOLD	- (RW) SDA Hold Time Length Register		22.2.26
+ *
+ *	Set the SDA hold time length register in I2C clocks.
+ */
+#define IG4_SDA_HOLD_MASK	0x00FF
+
+/*
+ * TX_ABRT_SOURCE- (RO) Transmit Abort Source Register		22.2.27
+ *
+ *	Indicates the cause of a transmit abort.  This can indicate a
+ *	software programming error or a device expected address width
+ *	mismatch or other issues.  The NORESTART conditions and GENCALL_NOACK
+ *	can only occur if a programming error was made in the driver software.
+ *
+ *	In particular, it should be possible to detect whether any devices
+ *	are on the bus by observing the GENCALL_READ status, and it might
+ *	be possible to detect ADDR7 vs ADDR10 mismatches.
+ */
+#define IG4_ABRTSRC_TRANSFER		0x00010000 /* Abort initiated by user */
+#define IG4_ABRTSRC_ARBLOST		0x00001000 /* Arbitration lost */
+#define IG4_ABRTSRC_NORESTART_10	0x00000400 /* RESTART disabled */
+#define IG4_ABRTSRC_NORESTART_START	0x00000200 /* RESTART disabled */
+#define IG4_ABRTSRC_ACKED_START		0x00000080 /* Improper acked START */
+#define IG4_ABRTSRC_GENCALL_NOACK	0x00000020 /* Improper GENCALL */
+#define IG4_ABRTSRC_GENCALL_READ	0x00000010 /* Nobody acked GENCALL */
+#define IG4_ABRTSRC_TXNOACK_DATA	0x00000008 /* data phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR10_2	0x00000004 /* addr10/1 phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR10_1	0x00000002 /* addr10/2 phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR7	0x00000001 /* addr7 phase no ACK */
+
+/*
+ * SLV_DATA_NACK - (RW) Generate Slave DATA NACK Register	22.2.28
+ *
+ *	When the controller is a receiver a NACK can be generated on
+ *	receipt of data.
+ *
+ *	NACK_GENERATE		Set to 0 for normal NACK/ACK generation.
+ *				Set to 1 to generate a NACK after next data
+ *				byte received.
+ *
+ */
+#define IG4_NACK_GENERATE	0x0001
+
+/*
+ * DMA_CTRL	- (RW) DMA Control Register			22.2.29
+ *
+ *	Enables DMA on the transmit and/or receive DMA channel.
+ */
+#define IG4_TX_DMA_ENABLE	0x0002
+#define IG4_RX_DMA_ENABLE	0x0001
+
+/*
+ * DMA_TDLR	- (RW) DMA Transmit Data Level Register		22.2.30
+ * DMA_RDLR	- (RW) DMA Receive Data Level Register		22.2.31
+ *
+ *	Similar to RX_TL and TX_TL but controls when a DMA burst occurs
+ *	to empty or fill the FIFOs.  Use the same IG4_FIFO_MASK and
+ *	IG4_FIFO_LIMIT defines for RX_RL and TX_TL.
+ */
+/* empty */
+
+/*
+ * SDA_SETUP	- (RW) SDA Setup Time Length Register		22.2.32
+ *
+ *	Set the SDA setup time length register in I2C clocks.
+ *	The register must be programmed with a value >=2.
+ *	(Defaults to 0x64).
+ */
+#define IG4_SDA_SETUP_MASK	0x00FF
+
+/*
+ * ACK_GEN_CALL	- (RW) ACK General Call Register		22.2.33
+ *
+ *	Control whether the controller responds with a ACK or NACK when
+ *	it receives an I2C General Call address.
+ *
+ *	If set to 0 a NACK is generated and a General Call interrupt is
+ *	NOT generated.  Otherwise an ACK + interrupt is generated.
+ */
+#define IG4_ACKGC_ACK		0x0001
+
+/*
+ * ENABLE_STATUS - (RO) Enable Status Registger			22.2.34
+ *
+ *	DATA_LOST	- Indicates that a slave receiver operation has
+ *			  been aborted with at least one data byte received
+ *			  from a transfer due to the I2C controller being
+ *			  disabled (IG4_I2C_ENABLE -> 0)
+ *
+ *	ENABLED		- Intel documentation is lacking but I assume this
+ *			  is a reflection of the IG4_I2C_ENABLE bit in the
+ *			  I2C_EN register.
+ *
+ */
+#define IG4_ENASTAT_DATA_LOST	0x0004
+#define IG4_ENASTAT_ENABLED	0x0001
+
+/*
+ * COMP_PARAM1 - (RO) Component Parameter Register		22.2.35
+ *		      Default Value 0x00FFFF6E
+ *
+ *	VALID		- Intel documentation is unclear but I believe this
+ *			  must be read as a 1 to indicate that the rest of
+ *			  the bits in the register are valid.
+ *
+ *	HASDMA		- Indicates that the chip is DMA-capable.  Presumably
+ *			  in certain virtualization cases the chip might be
+ *			  set to not be DMA-capable.
+ *
+ *	INTR_IO		- Indicates that all interrupts are combined to
+ *			  generate one interrupt.  If not set, interrupts
+ *			  are individual (more virtualization stuff?)
+ *
+ *	HCCNT_RO	- Indicates that the clock timing registers are
+ *			  RW.  If not set, the registers are RO.
+ *			  (more virtualization stuff).
+ *
+ *	MAXSPEED	- Indicates the maximum speed supported.
+ *
+ *	DATAW		- Indicates the internal bus width in bits.
+ */
+#define IG4_PARAM1_TXFIFO_DEPTH(v)	(((v) >> 16) & 0xFF)
+#define IG4_PARAM1_RXFIFO_DEPTH(v)	(((v) >> 8) & 0xFF)
+#define IG4_PARAM1_CONFIG_VALID		0x00000080
+#define IG4_PARAM1_CONFIG_HASDMA	0x00000040
+#define IG4_PARAM1_CONFIG_INTR_IO	0x00000020
+#define IG4_PARAM1_CONFIG_HCCNT_RO	0x00000010
+#define IG4_PARAM1_CONFIG_MAXSPEED_MASK	0x0000000C
+#define IG4_PARAM1_CONFIG_DATAW_MASK	0x00000003
+
+#define IG4_CONFIG_MAXSPEED_RESERVED00	0x00000000
+#define IG4_CONFIG_MAXSPEED_STANDARD	0x00000004
+#define IG4_CONFIG_MAXSPEED_FAST	0x00000008
+#define IG4_CONFIG_MAXSPEED_HIGH	0x0000000C
+
+#define IG4_CONFIG_DATAW_8		0x00000000
+#define IG4_CONFIG_DATAW_16		0x00000001
+#define IG4_CONFIG_DATAW_32		0x00000002
+#define IG4_CONFIG_DATAW_RESERVED11	0x00000003
+
+/*
+ * COMP_VER - (RO) Component Version Register			22.2.36
+ *		   Default Value 0x3131352A
+ *
+ *	Contains the chip version number.  All 32 bits.
+ */
+#define IG4_COMP_VER		0x3131352A
+
+/*
+ * COMP_TYPE - (RO) (linux) Endian and bus width probe
+ *
+ * 	Read32 from this register and test against IG4_COMP_TYPE
+ *	to determine the bus width.  e.g. 01404457 = endian-reversed,
+ *	and 00000140 or 00004457 means internal 16-bit bus (?).
+ *
+ *	This register is not in the intel documentation, I pulled it
+ *	from the linux driver i2c-designware-core.c.
+ */
+#define IG4_COMP_TYPE		0x44570140
+
+/*
+ * RESETS - (RW) Resets Register				22.2.37
+ *
+ *	Used to reset the I2C host controller by SW.  There is no timing
+ *	requirement, software can assert and de-assert in back-to-back
+ *	transactions.
+ *
+ *	00	I2C host controller is NOT in reset.
+ *	01	(reserved)
+ *	10	(reserved)
+ *	11	I2C host controller is in reset.
+ */
+#define IG4_RESETS_ASSERT	0x0003
+#define IG4_RESETS_DEASSERT	0x0000
+
+/*
+ * GENERAL - (RW) General Reigster				22.2.38
+ *
+ *	IOVOLT	0=1.8V 1=3.3V
+ *
+ *	LTR	0=Auto 1=SW
+ *
+ *	    In Auto mode the BIOS will write to the host controller's
+ *	    AUTO LTR Value register (offset 0x0814) with the active
+ *	    state LTR value, and will write to the SW LTR Value register
+ *	    (offset 0x0810) with the idle state LTR value.
+ *
+ *	    In SW mode the SW will write to the host controller SW LTR
+ *	    value (offset 0x0810).  It is the SW responsibility to update
+ *	    the LTR with the appropriate value.
+ */
+#define IG4_GENERAL_IOVOLT3_3	0x0008
+#define IG4_GENERAL_SWMODE	0x0004
+
+/*
+ * SW_LTR_VALUE - (RW) SW LTR Value Register			22.2.39
+ * AUTO_LTR_VALUE - (RW) SW LTR Value Register			22.2.40
+ *
+ *	Default value is 0x00000800 which means the best possible
+ *	service/response time.
+ *
+ *	It isn't quite clear how the snooping works.  There are two scale
+ *	bits for both sets but two of the four codes are reserved.  The
+ *	*SNOOP_VALUE() is specified as a 10-bit latency value.  If 0, it
+ *	indicates that the device cannot tolerate any delay and needs the
+ *	best possible service/response time.
+ *
+ *	I think this is for snooping (testing) the I2C bus.  The lowest
+ *	delay (0) probably runs the controller polling at a high, power hungry
+ *	rate.  But I dunno.
+ */
+#define IG4_SWLTR_NSNOOP_REQ		0x80000000	/* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_MASK	0x1C000000	/* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_1US	0x08000000	/* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_32US	0x0C000000	/* (ro) */
+#define IG4_SWLTR_NSNOOP_VALUE_DECODE(v) (((v) >> 16) & 0x3F)
+#define IG4_SWLTR_NSNOOP_VALUE_ENCODE(v) (((v) & 0x3F) << 16)
+
+#define IG4_SWLTR_SNOOP_REQ		0x00008000	/* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_MASK	0x00001C00	/* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_1US	0x00000800	/* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_32US	0x00000C00	/* (rw) */
+#define IG4_SWLTR_SNOOP_VALUE_DECODE(v)	 ((v) & 0x3F)
+#define IG4_SWLTR_SNOOP_VALUE_ENCODE(v)	 ((v) & 0x3F)
+
+#endif

Property changes on: sys/dev/ichiic/ig4_reg.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: sys/dev/ichiic/ig4_var.h
===================================================================
--- sys/dev/ichiic/ig4_var.h	(revision 0)
+++ sys/dev/ichiic/ig4_var.h	(working copy)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BUS_SMBUS_INTELGEN4_IG4_VAR_H_
+#define _BUS_SMBUS_INTELGEN4_IG4_VAR_H_
+
+#include "bus_if.h"
+#include "device_if.h"
+#include "pci_if.h"
+#include "smbus_if.h"
+
+#define IG4_RBUFSIZE	128
+#define IG4_RBUFMASK	(IG4_RBUFSIZE - 1)
+
+enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE };
+
+struct ig4iic_softc {
+	device_t	dev;
+	struct		intr_config_hook enum_hook;
+	device_t	smb;
+	struct resource	*regs_res;
+	int		regs_rid;
+	struct resource	*intr_res;
+	int		intr_rid;
+	void		*intr_handle;
+	int		intr_type;
+	enum ig4_op	op;
+	int		cmd;
+	int		rnext;
+	int		rpos;
+	char		rbuf[IG4_RBUFSIZE];
+	int		error;
+	uint8_t		last_slave;
+	int		pci_attached : 1;
+	int		use_10bit : 1;
+	int		slave_valid : 1;
+	int		read_started : 1;
+	int		write_started : 1;
+
+	/*
+	 * Locking semantics:
+	 *
+	 * Functions implementing the smbus interface that interact
+	 * with the controller acquire an exclusive lock on call_lock
+	 * to prevent interleaving of calls to the interface and a lock on
+	 * io_lock right afterwards, to synchronize controller I/O activity.
+	 * 
+	 * The interrupt handler can only read data while no ig4iic_smb_* call
+	 * is in progress or while io_lock is dropped during mtx_sleep in
+	 * wait_status and set_controller. It is safe to drop io_lock in those
+	 * places, because the interrupt handler only accesses those registers:
+	 *
+	 * - IG4_REG_I2C_STA  (I2C Status)
+	 * - IG4_REG_DATA_CMD (Data Buffer and Command)
+	 * - IG4_REG_CLR_INTR (Clear Interrupt)
+	 *
+	 * Locking outside of those places is required to make the content
+	 * of rpos/rnext predictable (e.g. whenever data_read is called and in
+	 * smb_transaction).
+	 */
+	struct sx	call_lock;
+	struct mtx	io_lock;
+};
+
+typedef struct ig4iic_softc ig4iic_softc_t;
+
+/* Attach/Detach called from ig4iic_pci_*() */
+int ig4iic_attach(ig4iic_softc_t *sc);
+int ig4iic_detach(ig4iic_softc_t *sc);
+
+/* SMBus methods */
+extern smbus_callback_t ig4iic_smb_callback;
+extern smbus_quick_t    ig4iic_smb_quick;
+extern smbus_sendb_t    ig4iic_smb_sendb;
+extern smbus_recvb_t    ig4iic_smb_recvb;
+extern smbus_writeb_t   ig4iic_smb_writeb;
+extern smbus_writew_t   ig4iic_smb_writew;
+extern smbus_readb_t    ig4iic_smb_readb;
+extern smbus_readw_t    ig4iic_smb_readw;
+extern smbus_pcall_t    ig4iic_smb_pcall;
+extern smbus_bwrite_t   ig4iic_smb_bwrite;
+extern smbus_bread_t    ig4iic_smb_bread;
+extern smbus_trans_t    ig4iic_smb_trans;
+
+#endif

Property changes on: sys/dev/ichiic/ig4_var.h
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: sys/dev/isl/isl.c
===================================================================
--- sys/dev/isl/isl.c	(revision 0)
+++ sys/dev/isl/isl.c	(working copy)
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for intersil I2C ISL29018 Digital Ambient Light Sensor and Proximity
+ * Sensor with Interrupt Function, only tested connected over SMBus (ig4iic).
+ *
+ * Datasheet:
+ * http://www.intersil.com/en/products/optoelectronics/ambient-light-and-proximity-sensors/light-to-digital-sensors/ISL29018.html
+ * http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29018.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/event.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/lockmgr.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/poll.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <dev/smbus/smbconf.h>
+#include <dev/smbus/smbus.h>
+#include <dev/isl/isl.h>
+
+#include "smbus_if.h"
+#include "bus_if.h"
+#include "device_if.h"
+
+#define ISL_METHOD_ALS		0x10
+#define ISL_METHOD_IR		0x11
+#define ISL_METHOD_PROX		0x12
+#define ISL_METHOD_RESOLUTION	0x13
+#define ISL_METHOD_RANGE	0x14
+
+struct isl_softc {
+	device_t	dev;
+	int		addr;
+
+	struct sx	isl_sx;
+};
+
+/* Returns < 0 on problem. */
+static int isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask);
+
+/*
+ * Initialize the device
+ */
+static int
+init_device(device_t dev, int addr, int probe)
+{
+	static char bl_init[] = { 0x00 };
+
+	device_t bus;
+	int error;
+
+	bus = device_get_parent(dev);	/* smbus */
+
+	/*
+	 * init procedure: send 0x00 to test ref and cmd reg 1
+	 */
+	error = smbus_trans(bus, addr, REG_TEST,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    bl_init, sizeof(bl_init), NULL, 0, NULL);
+	if (error)
+		goto done;
+
+	error = smbus_trans(bus, addr, REG_CMD1,
+			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+			    bl_init, sizeof(bl_init), NULL, 0, NULL);
+	if (error)
+		goto done;
+
+	pause("islinit", hz/100);
+
+done:
+	if (error)
+		device_printf(dev, "Unable to initialize\n");
+	return (error);
+}
+
+static int isl_probe(device_t);
+static int isl_attach(device_t);
+static int isl_detach(device_t);
+
+static int isl_sysctl(SYSCTL_HANDLER_ARGS);
+
+static devclass_t isl_devclass;
+
+static device_method_t isl_methods[] = {
+	/* device interface */
+	DEVMETHOD(device_probe,		isl_probe),
+	DEVMETHOD(device_attach,	isl_attach),
+	DEVMETHOD(device_detach,	isl_detach),
+
+	DEVMETHOD_END
+};
+
+static driver_t isl_driver = {
+	"isl",
+	isl_methods,
+	sizeof(struct isl_softc),
+};
+
+static int
+isl_probe(device_t dev)
+{
+	int addr;
+	int error;
+
+	addr = smbus_get_addr(dev);
+
+	/*
+	 * 0x44 - isl ambient light sensor on the acer c720.
+	 * (other devices might use other ids).
+	 */
+	if (addr != 0x44)
+		return (ENXIO);
+
+	error = init_device(dev, addr, 1);
+	if (error)
+		return (ENXIO);
+
+	device_set_desc(dev, "ISL Digital Ambient Light Sensor");
+
+	return (BUS_PROBE_VENDOR);
+}
+
+static int
+isl_attach(device_t dev)
+{
+	struct isl_softc *sc;
+	struct sysctl_ctx_list *sysctl_ctx;
+	struct sysctl_oid *sysctl_tree;
+	int addr;
+	int use_als;
+	int use_ir;
+	int use_prox;
+
+	sc = device_get_softc(dev);
+
+	if (!sc)
+		return (ENOMEM);
+
+	addr = smbus_get_addr(dev);
+
+	if (init_device(dev, addr, 0))
+		return (ENXIO);
+
+	sx_init(&sc->isl_sx, "ISL read lock");
+
+	sc->dev = dev;
+	sc->addr = addr;
+
+	sysctl_ctx = device_get_sysctl_ctx(dev);
+	sysctl_tree = device_get_sysctl_tree(dev);
+
+	use_als = isl_read_sensor(dev, addr, CMD1_MASK_ALS_ONCE) >= 0;
+	use_ir = isl_read_sensor(dev, addr, CMD1_MASK_IR_ONCE) >= 0;
+	use_prox = isl_read_sensor(dev, addr, CMD1_MASK_PROX_ONCE) >= 0;
+
+	if (use_als) {
+		SYSCTL_ADD_PROC(sysctl_ctx,
+	 		SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+			    "als", CTLTYPE_INT | CTLFLAG_RD,
+			    sc, ISL_METHOD_ALS, isl_sysctl, "I",
+			    "Current ALS sensor read-out");
+	}
+
+	if (use_ir) {
+		SYSCTL_ADD_PROC(sysctl_ctx,
+			SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+			    "ir", CTLTYPE_INT | CTLFLAG_RD,
+			    sc, ISL_METHOD_IR, isl_sysctl, "I",
+			    "Current IR sensor read-out");
+	}
+
+	if (use_prox) {
+		SYSCTL_ADD_PROC(sysctl_ctx,
+			SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+			    "prox", CTLTYPE_INT | CTLFLAG_RD,
+			    sc, ISL_METHOD_PROX, isl_sysctl, "I",
+			    "Current proximity sensor read-out");
+	}
+
+	SYSCTL_ADD_PROC(sysctl_ctx,
+		SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+		    "resolution", CTLTYPE_INT | CTLFLAG_RD,
+		    sc, ISL_METHOD_RESOLUTION, isl_sysctl, "I",
+		    "Current proximity sensor resolution");
+
+	SYSCTL_ADD_PROC(sysctl_ctx,
+	SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
+	    "range", CTLTYPE_INT | CTLFLAG_RD,
+	    sc, ISL_METHOD_RANGE, isl_sysctl, "I",
+	    "Current proximity sensor range");
+
+	return (0);
+}
+
+static int
+isl_detach(device_t dev)
+{
+	struct isl_softc *sc;
+
+	sc = device_get_softc(dev);
+	sx_destroy(&sc->isl_sx);
+
+	return (0);
+}
+
+static int
+isl_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	static int resolutions[] = { 16, 12, 8, 4};
+	static int ranges[] = { 1000, 4000, 16000, 64000};
+
+	struct isl_softc *sc;
+	device_t bus;
+	uint8_t rbyte;
+	int arg;
+	int resolution;
+	int range;
+
+	sc = (struct isl_softc *)oidp->oid_arg1;
+	arg = -1;
+
+	sx_xlock(&sc->isl_sx);
+	bus = device_get_parent(sc->dev);	/* smbus */
+	if (smbus_trans(bus, sc->addr, REG_CMD2,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, &rbyte, sizeof(rbyte), NULL)) {
+		sx_xunlock(&sc->isl_sx);
+		return (-1);
+	}
+	resolution = resolutions[(rbyte & CMD2_MASK_RESOLUTION)
+			    >> CMD2_SHIFT_RESOLUTION];
+	range = ranges[(rbyte & CMD2_MASK_RANGE) >> CMD2_SHIFT_RANGE];
+
+	switch (oidp->oid_arg2) {
+	case ISL_METHOD_ALS:
+		arg = (isl_read_sensor(sc->dev, sc->addr,
+		    CMD1_MASK_ALS_ONCE) * range) >> resolution;
+		break;
+	case ISL_METHOD_IR:
+		arg = isl_read_sensor(sc->dev, sc->addr,
+		    CMD1_MASK_IR_ONCE);
+		break;
+	case ISL_METHOD_PROX:
+		arg = isl_read_sensor(sc->dev, sc->addr,
+		    CMD1_MASK_PROX_ONCE);
+		break;
+	case ISL_METHOD_RESOLUTION:
+		arg = (1 << resolution);
+		break;
+	case ISL_METHOD_RANGE:
+		arg = range;
+		break;
+	}
+	sx_xunlock(&sc->isl_sx);
+
+	SYSCTL_OUT(req, &arg, sizeof(arg));
+	return (0);
+}
+
+static int
+isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask)
+{
+	device_t bus;
+	uint8_t rbyte;
+	uint8_t cmd;
+	int ret;
+
+	bus = device_get_parent(dev);	/* smbus */
+
+	if (smbus_trans(bus, addr, REG_CMD1,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, &rbyte, sizeof(rbyte), NULL)) {
+		device_printf(dev,
+		    "Couldn't read first byte before issuing command %d\n",
+		    cmd_mask);
+		return (-1);
+	}
+
+	cmd = (rbyte & 0x1f) | cmd_mask;
+	if (smbus_trans(bus, addr, REG_CMD1,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    &cmd, sizeof(cmd), NULL, 0, NULL)) {
+		device_printf(dev, "Couldn't write command %d\n", cmd_mask);
+		return (-1);
+	}
+
+	pause("islconv", hz/10);
+
+	if (smbus_trans(bus, addr, REG_DATA1,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, &rbyte, sizeof(rbyte), NULL)) {
+		device_printf(dev,
+		    "Couldn't read first byte after command %d\n", cmd_mask);
+		return (-1);
+	}
+
+	ret = rbyte;
+	if (smbus_trans(bus, addr, REG_DATA2,
+	    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
+	    NULL, 0, &rbyte, sizeof(rbyte), NULL)) {
+		device_printf(dev, "Couldn't read second byte after command %d\n", cmd_mask);
+		return (-1);
+	}
+	ret += rbyte << 8;
+
+	return (ret);
+}
+
+DRIVER_MODULE(isl, smbus, isl_driver, isl_devclass, NULL, NULL);
+MODULE_DEPEND(isl, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
+MODULE_VERSION(isl, 1);

Property changes on: sys/dev/isl/isl.c
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: sys/dev/isl/isl.h
===================================================================
--- sys/dev/isl/isl.h	(revision 0)
+++ sys/dev/isl/isl.h	(working copy)
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _ISL_H_
+#define _ISL_H_
+
+/* Command register 1  (bits 7-5) */
+#define REG_CMD1		0x00
+#define CMD1_MASK_POWER_DOWN	0x00      /* 00000000 */
+#define CMD1_MASK_ALS_ONCE	0x01 << 5 /* 00100000 */
+#define CMD1_MASK_IR_ONCE	0x02 << 5 /* 01000000 */
+#define CMD1_MASK_PROX_ONCE	0x03 << 5 /* 01100000 */
+/* RESERVED */                            /* 10000000 */
+#define CMD1_MASK_ALS_CONT	0x05 << 5 /* 10100000 */
+#define CMD1_MASK_IR_CONT	0x06 << 5 /* 11000000 */
+#define CMD1_MASK_PROX_CONT	0x07 << 5 /* 11100000 */
+
+/* Command register 2 (bits) */
+#define REG_CMD2		0x01
+
+/* data registers */
+#define REG_DATA1		0x02
+#define REG_DATA2		0x03
+#define CMD2_SHIFT_RANGE	0x00
+#define CMD2_MASK_RANGE		(0x03 << CMD2_SHIFT_RANGE)
+#define CMD2_SHIFT_RESOLUTION	0x02
+#define CMD2_MASK_RESOLUTION	(0x03 << CMD2_SHIFT_RESOLUTION)
+
+/* Interrupt registers */
+#define REG_INT_LO_LSB		0x04
+#define REG_INT_LO_MSB		0x05
+#define REG_INT_HI_LSB		0x06
+#define REG_INT_HI_MSB		0x07
+
+/* Test register (should hold 0x00 at all times */
+#define REG_TEST		0x08
+
+#endif

Property changes on: sys/dev/isl/isl.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: sys/dev/smbus/smb.c
===================================================================
--- sys/dev/smbus/smb.c	(revision 287980)
+++ sys/dev/smbus/smb.c	(working copy)
@@ -26,10 +26,6 @@
  * $FreeBSD$
  */
 
-#ifdef HAVE_KERNEL_OPTION_HEADERS
-#include "opt_compat.h"
-#endif
-
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
@@ -104,9 +100,11 @@
 static int
 smb_probe(device_t dev)
 {
+	if (smbus_get_addr(dev) != -1)
+		return (ENXIO);
+
 	device_set_desc(dev, "SMBus generic I/O");
-
-	return (0);
+	return (BUS_PROBE_NOWILDCARD);
 }
 	
 static int
@@ -113,10 +111,13 @@
 smb_attach(device_t dev)
 {
 	struct smb_softc *sc = device_get_softc(dev);
+	int unit;
+	
+	unit = device_get_unit(dev);
+	sc->sc_dev = dev;
 
-	sc->sc_dev = dev;
-	sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
-	    UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev));
+	sc->sc_devnode = make_dev(&smb_cdevsw, unit, UID_ROOT, GID_WHEEL,
+	    0600, "smb%d", unit);
 	sc->sc_devnode->si_drv1 = sc;
 	mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF);
 
@@ -174,10 +175,17 @@
 	struct smb_softc *sc = dev->si_drv1;
 	device_t smbdev = sc->sc_dev;
 	int error;
-	short w;
-	u_char count;
-	char c;
+	int unit;
+	u_char bcount;
 
+	/*
+	 * If a specific slave device is being used, override any passed-in
+	 * slave.
+	 */
+	unit = dev2unit(dev);
+	if (unit & 0x0400)
+		s->slave = unit & 0x03ff;
+
 	parent = device_get_parent(smbdev);
 
 	/* Make sure that LSB bit is cleared. */
@@ -208,77 +216,101 @@
 
 	case SMB_WRITEB:
 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
-						s->data.byte));
+						s->wdata.byte));
 		break;
 
 	case SMB_WRITEW:
 		error = smbus_error(smbus_writew(parent, s->slave,
-						s->cmd, s->data.word));
+						s->cmd, s->wdata.word));
 		break;
 
 	case SMB_READB:
-		if (s->data.byte_ptr) {
-			error = smbus_error(smbus_readb(parent, s->slave,
-						s->cmd, &c));
-			if (error)
-				break;
-			error = copyout(&c, s->data.byte_ptr,
-					sizeof(*(s->data.byte_ptr)));
+		error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
+		    &s->rdata.byte));
+		if (error)
+			break;
+		if (s->rbuf && s->rcount >= 1) {
+			error = copyout(&s->rdata.byte, s->rbuf, 1);
+			s->rcount = 1;
 		}
 		break;
 
 	case SMB_READW:
-		if (s->data.word_ptr) {
-			error = smbus_error(smbus_readw(parent, s->slave,
-						s->cmd, &w));
-			if (error == 0) {
-				error = copyout(&w, s->data.word_ptr,
-						sizeof(*(s->data.word_ptr)));
-			}
+		error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
+		    &s->rdata.word));
+		if (error)
+			break;
+		if (s->rbuf && s->rcount >= 2) {
+			buf[0] = (u_char)s->rdata.word;
+			buf[1] = (u_char)(s->rdata.word >> 8);
+			error = copyout(buf, s->rbuf, 2);
+			s->rcount = 2;
 		}
 		break;
 
 	case SMB_PCALL:
-		if (s->data.process.rdata) {
+		error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
+		    s->wdata.word, &s->rdata.word));
+		if (error)
+			break;
+		if (s->rbuf && s->rcount >= 2) {
+			buf[0] = (u_char)s->rdata.word;
+			buf[1] = (u_char)(s->rdata.word >> 8);
+			error = copyout(buf, s->rbuf, 2);
+			s->rcount = 2;
+		}
 
-			error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
-				s->data.process.sdata, &w));
-			if (error)
-				break;
-			error = copyout(&w, s->data.process.rdata,
-					sizeof(*(s->data.process.rdata)));
-		}
-		
 		break;
 
 	case SMB_BWRITE:
-		if (s->count && s->data.byte_ptr) {
-			if (s->count > SMB_MAXBLOCKSIZE)
-				s->count = SMB_MAXBLOCKSIZE;
-			error = copyin(s->data.byte_ptr, buf, s->count);
-			if (error)
-				break;
-			error = smbus_error(smbus_bwrite(parent, s->slave,
-						s->cmd, s->count, buf));
+		if (s->wcount < 0) {
+			error = EINVAL;
+			break;
 		}
+		if (s->wcount > SMB_MAXBLOCKSIZE)
+			s->wcount = SMB_MAXBLOCKSIZE;
+		if (s->wcount)
+			error = copyin(s->wbuf, buf, s->wcount);
+		if (error)
+			break;
+		error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
+		    s->wcount, buf));
 		break;
 
-#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
-	case SMB_OLD_BREAD:
-#endif
 	case SMB_BREAD:
-		if (s->count && s->data.byte_ptr) {
-			count = min(s->count, SMB_MAXBLOCKSIZE);
-			error = smbus_error(smbus_bread(parent, s->slave,
-						s->cmd, &count, buf));
-			if (error)
-				break;
-			error = copyout(buf, s->data.byte_ptr,
-			    min(count, s->count));
-			s->count = count;
+		if (s->rcount < 0) {
+			error = EINVAL;
+			break;
 		}
+		if (s->rcount > SMB_MAXBLOCKSIZE)
+			s->rcount = SMB_MAXBLOCKSIZE;
+		error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
+		    &bcount, buf));
+		if (error)
+			break;
+		if (s->rcount > bcount)
+			s->rcount = bcount;
+		error = copyout(buf, s->rbuf, s->rcount);
 		break;
-		
+
+	case SMB_TRANS:
+		if (s->rcount < 0 || s->wcount < 0) {
+			error = EINVAL;
+			break;
+		}
+		if (s->rcount > SMB_MAXBLOCKSIZE)
+			s->rcount = SMB_MAXBLOCKSIZE;
+		if (s->wcount > SMB_MAXBLOCKSIZE)
+			s->wcount = SMB_MAXBLOCKSIZE;
+		if (s->wcount)
+			error = copyin(s->wbuf, buf, s->wcount);
+		if (error)
+			break;
+		error = smbus_error(smbus_trans(parent, s->slave, s->cmd,
+		    s->op, buf, s->wcount, buf, s->rcount, &s->rcount));
+		if (error == 0)
+			error = copyout(buf, s->rbuf, s->rcount);
+		break;
 	default:
 		error = ENOTTY;
 	}
Index: sys/dev/smbus/smb.h
===================================================================
--- sys/dev/smbus/smb.h	(revision 287980)
+++ sys/dev/smbus/smb.h	(working copy)
@@ -32,27 +32,33 @@
 #include <sys/ioccom.h>
 
 struct smbcmd {
-	char cmd;
-	int count;
-	u_char slave;
+	u_char cmd;
+	u_char reserved;
+	u_short op;
 	union {
-		char byte;
-		short word;
-
-		char *byte_ptr;
-		short *word_ptr;
-
-		struct {
-			short sdata;
-			short *rdata;
-		} process;
-	} data;
+		char	byte;
+		char	buf[2];
+		short	word;
+	} wdata;
+	union {
+		char	byte;
+		char	buf[2];
+		short	word;
+	} rdata;
+	int  slave;
+	char *wbuf;	/* use wdata if NULL */
+	int  wcount;
+	char *rbuf;	/* use rdata if NULL */
+	int  rcount;
 };
 
 /*
  * SMBus spec 2.0 says block transfers may be at most 32 bytes.
+ * We use SMBus for i2c as well, make the size limit something more
+ * reasonable.  Keep in mind that a char buf array is declared on the
+ * kernel stack.
  */
-#define SMB_MAXBLOCKSIZE	32
+#define SMB_MAXBLOCKSIZE	1024
 
 #define SMB_QUICK_WRITE	_IOW('i', 1, struct smbcmd)
 #define SMB_QUICK_READ	_IOW('i', 2, struct smbcmd)
@@ -66,5 +72,6 @@
 #define SMB_BWRITE	_IOW('i', 10, struct smbcmd)
 #define SMB_OLD_BREAD	_IOW('i', 11, struct smbcmd)
 #define SMB_BREAD	_IOWR('i', 11, struct smbcmd)
+#define SMB_TRANS	_IOWR('i', 12, struct smbcmd)
 
 #endif
Index: sys/dev/smbus/smbconf.h
===================================================================
--- sys/dev/smbus/smbconf.h	(revision 287980)
+++ sys/dev/smbus/smbconf.h	(working copy)
@@ -68,9 +68,30 @@
 #define SMB_QREAD	0x1
 
 /*
+ * smbus transction op with pass-thru capabilities
+ *
+ * This smbus function is capable of doing a smbus command transaction
+ * (read or write), and can be flagged to not issue the 'cmd' and/or
+ * issue or expect a count field as well as flagged for chaining (no STOP),
+ * which gives it an i2c pass-through capability.
+ *
+ * NOSTOP- Caller chaining transactions, do not issue STOP
+ * NOCMD-  Do not transmit the command field
+ * NOCNT-  Do not transmit (wr) or expect (rd) the count field
+ */
+#define SMB_TRANS_NOSTOP  0x0001  /* do not send STOP at end */
+#define SMB_TRANS_NOCMD   0x0002  /* ignore cmd field (do not tx) */ 
+#define SMB_TRANS_NOCNT   0x0004  /* do not tx or rx count field */  
+#define SMB_TRANS_7BIT    0x0008  /* change address mode to 7-bit */ 
+#define SMB_TRANS_10BIT   0x0010  /* change address mode to 10-bit */
+#define SMB_TRANS_NOREPORT  0x0020  /* do not report errors */
+
+/*
  * ivars codes
  */
-#define SMBUS_IVAR_ADDR	0x1	/* slave address of the device */
+enum smbus_ivars {
+    SMBUS_IVAR_ADDR,	/* slave address of the device */
+};
 
 int	smbus_request_bus(device_t, device_t, int);
 int	smbus_release_bus(device_t, device_t);
@@ -79,8 +100,13 @@
 
 void	smbus_intr(device_t, u_char, char low, char high, int error);
 
-u_char	smbus_get_addr(device_t);
+#define SMBUS_ACCESSOR(var, ivar, type)					\
+	__BUS_ACCESSOR(smbus, var, SMBUS, ivar, type)
 
+SMBUS_ACCESSOR(addr,		ADDR,		int)
+
+#undef SMBUS_ACCESSOR
+
 extern driver_t smbus_driver;
 extern devclass_t smbus_devclass;
 
@@ -104,6 +130,9 @@
 	(SMBUS_BWRITE(device_get_parent(bus), slave, cmd, count, buf))
 #define smbus_bread(bus,slave,cmd,count,buf) \
 	(SMBUS_BREAD(device_get_parent(bus), slave, cmd, count, buf))
+#define smbus_trans(bus,slave,cmd,op,wbuf,wcount,rbuf,rcount,actualp) \
+	(SMBUS_TRANS(device_get_parent(bus), slave, cmd, op, \
+	wbuf, wcount, rbuf, rcount, actualp))
 
 #define SMBUS_MODVER	1
 #define SMBUS_MINVER	1
Index: sys/dev/smbus/smbus.c
===================================================================
--- sys/dev/smbus/smbus.c	(revision 287980)
+++ sys/dev/smbus/smbus.c	(working copy)
@@ -33,11 +33,15 @@
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
-#include <sys/bus.h> 
+#include <sys/bus.h>
 
 #include <dev/smbus/smbconf.h>
 #include <dev/smbus/smbus.h>
 
+#include "smbus_if.h"
+#include "bus_if.h"
+
+
 /*
  * Autoconfiguration and support routines for System Management bus
  */
@@ -49,6 +53,13 @@
 static int smbus_attach(device_t);
 static int smbus_detach(device_t);
 
+static int smbus_child_location_str(device_t parent, device_t child,
+		    char *buf, size_t buflen);
+static int smbus_print_child(device_t parent, device_t child);
+static void smbus_probe_device(device_t dev, u_char* addr);
+static int smbus_read_ivar(device_t parent, device_t child, int which,
+		    uintptr_t *result);
+
 static device_method_t smbus_methods[] = {
         /* device interface */
         DEVMETHOD(device_probe,         smbus_probe),
@@ -57,6 +68,10 @@
 
 	/* bus interface */
 	DEVMETHOD(bus_add_child,	bus_generic_add_child),
+	DEVMETHOD(bus_child_location_str, smbus_child_location_str),
+	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
+	DEVMETHOD(bus_print_child,	smbus_print_child),
+	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
 
 	DEVMETHOD_END
 };
@@ -87,9 +102,14 @@
 smbus_attach(device_t dev)
 {
 	struct smbus_softc *sc = device_get_softc(dev);
+	unsigned char addr;
 
 	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
 	bus_generic_probe(dev);
+	for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) {
+		sc->addrs[addr] = addr;
+		smbus_probe_device(dev, &sc->addrs[addr]);
+	}
 	bus_generic_attach(dev);
 
 	return (0);
@@ -114,4 +134,70 @@
 {
 }
 
+static void
+smbus_probe_device(device_t dev, u_char* addr)
+{
+	device_t child;
+	int error;
+	u_char cmd;
+	u_char buf[2];
+
+	cmd = 0x01;
+	error = smbus_trans(dev, *addr, cmd,
+			    SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT,
+			    NULL, 0, buf, 1, NULL);
+	if (error == 0) {
+		if (bootverbose)
+			device_printf(dev, "Probed address 0x%02x\n", *addr);
+		child = device_add_child(dev, NULL, -1);
+		device_set_ivars(child, addr);
+	}
+}
+
+static int
+smbus_child_location_str(device_t parent, device_t child, char *buf,
+    size_t buflen)
+{
+	unsigned char *addr;
+
+	addr = device_get_ivars(child);
+	if (addr)
+		snprintf(buf, buflen, "addr=0x%x", *addr);
+	else if (buflen)
+		buf[0] = 0;
+	return (0);
+}
+
+static int
+smbus_print_child(device_t parent, device_t child)
+{
+	unsigned char *addr;
+	int retval;
+
+	addr = device_get_ivars(child);
+	retval = bus_print_child_header(parent, child);
+	if (addr)
+		retval += printf(" at addr 0x%x", *addr);
+	retval += bus_print_child_footer(parent, child);
+
+	return (retval);
+}
+
+static int
+smbus_read_ivar(device_t parent, device_t child, int which,
+    uintptr_t *result)
+{
+	unsigned char *addr;
+
+	addr = device_get_ivars(child);
+	switch (which) {
+	case SMBUS_IVAR_ADDR:
+		*result = (addr == NULL) ? -1 : *addr;
+		break;
+	default:
+		return (ENOENT);
+	}
+	return (0);
+}
+
 MODULE_VERSION(smbus, SMBUS_MODVER);
Index: sys/dev/smbus/smbus.h
===================================================================
--- sys/dev/smbus/smbus.h	(revision 287980)
+++ sys/dev/smbus/smbus.h	(working copy)
@@ -29,9 +29,13 @@
 #ifndef __SMBUS_H
 #define __SMBUS_H
 
+#define SMBUS_ADDR_MIN	0x10
+#define SMBUS_ADDR_MAX	0x70
+
 struct smbus_softc {
 	device_t owner;		/* smbus owner device structure */
 	struct mtx lock;
+	unsigned char addrs[SMBUS_ADDR_MAX];
 };
 
 void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err);
Index: sys/dev/smbus/smbus_if.m
===================================================================
--- sys/dev/smbus/smbus_if.m	(revision 287980)
+++ sys/dev/smbus/smbus_if.m	(working copy)
@@ -149,3 +149,20 @@
 	u_char *count;
 	char *buf;
 };
+
+#
+# SMB roll-up transaction with flags that also allow it to be     
+# used for (mostly) i2c pass-through and with 10-bit addresses.   
+# This function can be used to roll-up all of the above functions.
+#
+METHOD int trans {       
+	device_t dev;    
+	int     slave;   
+	char    cmd;     
+	int     op;      
+	char    *wbuf;   
+	int     wcount;  
+	char    *rbuf;   
+	int     rcount;  
+	int     *actualp;
+};
Index: sys/modules/i2c/Makefile
===================================================================
--- sys/modules/i2c/Makefile	(revision 287980)
+++ sys/modules/i2c/Makefile	(working copy)
@@ -1,6 +1,6 @@
 # $FreeBSD$
 
 SUBDIR =
-SUBDIR += controllers if_ic smbus iicbus iicbb iicsmb iic smb
+SUBDIR += controllers if_ic smbus iicbus iicbb iicsmb iic cyapa smb isl
 
 .include <bsd.subdir.mk>
Index: sys/modules/i2c/controllers/Makefile
===================================================================
--- sys/modules/i2c/controllers/Makefile	(revision 287980)
+++ sys/modules/i2c/controllers/Makefile	(working copy)
@@ -3,7 +3,7 @@
 .if ${MACHINE} == "pc98"
 SUBDIR = lpbb
 .else
-SUBDIR = alpm amdpm amdsmb ichsmb intpm nfsmb viapm lpbb pcf
+SUBDIR = alpm amdpm amdsmb ichiic ichsmb intpm nfsmb viapm lpbb pcf
 .endif
 
 .include <bsd.subdir.mk>
Index: sys/modules/i2c/controllers/ichiic/Makefile
===================================================================
--- sys/modules/i2c/controllers/ichiic/Makefile	(revision 0)
+++ sys/modules/i2c/controllers/ichiic/Makefile	(working copy)
@@ -0,0 +1,8 @@
+#$FreeBSD$
+
+.PATH:		${.CURDIR}/../../../../dev/ichiic
+KMOD		= ig4
+SRCS		= device_if.h bus_if.h iicbb_if.h pci_if.h smbus_if.h \
+		  ig4_iic.c ig4_pci.c ig4_reg.h ig4_var.h
+
+.include <bsd.kmod.mk>

Property changes on: sys/modules/i2c/controllers/ichiic/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: sys/modules/i2c/cyapa/Makefile
===================================================================
--- sys/modules/i2c/cyapa/Makefile	(revision 0)
+++ sys/modules/i2c/cyapa/Makefile	(working copy)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH:		${.CURDIR}/../../../dev/cyapa
+KMOD		= cyapa
+SRCS		= cyapa.c device_if.h bus_if.h smbus_if.h vnode_if.h
+
+.include <bsd.kmod.mk>

Property changes on: sys/modules/i2c/cyapa/Makefile
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: sys/modules/i2c/isl/Makefile
===================================================================
--- sys/modules/i2c/isl/Makefile	(revision 0)
+++ sys/modules/i2c/isl/Makefile	(working copy)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH:		${.CURDIR}/../../../dev/isl
+KMOD		= isl
+SRCS		= isl.c device_if.h bus_if.h smbus_if.h vnode_if.h
+
+.include <bsd.kmod.mk>

Property changes on: sys/modules/i2c/isl/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: usr.sbin/smbmsg/smbmsg.c
===================================================================
--- usr.sbin/smbmsg/smbmsg.c	(revision 287980)
+++ usr.sbin/smbmsg/smbmsg.c	(working copy)
@@ -163,7 +163,8 @@
 	}
 	if (iflag == 1 && oflag == -1) {
 		/* command + 1 byte input: read byte op. */
-		c.data.byte_ptr = ibuf;
+		c.rbuf = ibuf;
+		c.rcount = iflag;
 		if (ioctl(fd, SMB_READB, &c) == -1)
 			return (-1);
 		printf(fmt, (int)(unsigned char)ibuf[0]);
@@ -171,11 +172,12 @@
 		return (0);
 	} else if (iflag == -1 && oflag == 1) {
 		/* command + 1 byte output: write byte op. */
-		c.data.byte = obuf[0];
+		c.wdata.byte = obuf[0];
 		return (ioctl(fd, SMB_WRITEB, &c));
 	} else if (wflag && iflag == 2 && oflag == -1) {
 		/* command + 2 bytes input: read word op. */
-		c.data.word_ptr = &iword;
+		c.rbuf = (char*) &iword;
+		c.rcount = iflag;
 		if (ioctl(fd, SMB_READW, &c) == -1)
 			return (-1);
 		printf(fmt, (int)(unsigned short)iword);
@@ -183,7 +185,7 @@
 		return (0);
 	} else if (wflag && iflag == -1 && oflag == 2) {
 		/* command + 2 bytes output: write word op. */
-		c.data.word = oword;
+		c.wdata.word = oword;
 		return (ioctl(fd, SMB_WRITEW, &c));
 	} else if (wflag && iflag == 2 && oflag == 2) {
 		/*
@@ -190,8 +192,9 @@
 		 * command + 2 bytes output + 2 bytes input:
 		 * "process call" op.
 		 */
-		c.data.process.sdata = oword;
-		c.data.process.rdata = &iword;
+		c.wdata.word = oword;
+		c.rbuf = (char*) &iword;
+		c.rcount = iflag;
 		if (ioctl(fd, SMB_PCALL, &c) == -1)
 			return (-1);
 		printf(fmt, (int)(unsigned short)iword);
@@ -199,8 +202,8 @@
 		return (0);
 	} else if (iflag > 1 && oflag == -1) {
 		/* command + > 1 bytes of input: block read */
-		c.data.byte_ptr = ibuf;
-		c.count = iflag;
+		c.rbuf = ibuf;
+		c.rcount = iflag;
 		if (ioctl(fd, SMB_BREAD, &c) == -1)
 			return (-1);
 		for (i = 0; i < iflag; i++) {
@@ -212,8 +215,8 @@
 		return (0);
 	} else if (iflag == -1 && oflag > 1) {
 		/* command + > 1 bytes of output: block write */
-		c.data.byte_ptr = obuf;
-		c.count = oflag;
+		c.wbuf = obuf;
+		c.wcount = oflag;
 		return (ioctl(fd, SMB_BWRITE, &c));
 	}
 
