Index: boot/i386/libi386/biosdisk.c =================================================================== --- boot/i386/libi386/biosdisk.c (revision 272526) +++ boot/i386/libi386/biosdisk.c (working copy) @@ -496,8 +496,9 @@ /* Decide whether we have to bounce */ if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 && + (VTOP(dest + blks * BD(dev).bd_sectorsize) >= 16384 * 1024 || (VTOP(dest) >> 16) != (VTOP(dest + - blks * BD(dev).bd_sectorsize) >> 16))) { + blks * BD(dev).bd_sectorsize) >> 16)))) { /* * There is a 64k physical boundary somewhere in the Index: boot/i386/libi386/biosmem.c =================================================================== --- boot/i386/libi386/biosmem.c (revision 272526) +++ boot/i386/libi386/biosmem.c (working copy) @@ -49,6 +49,7 @@ bios_getmem(void) { uint64_t size; + int64_t v; /* Parse system memory map */ v86.ebx = 0; @@ -109,7 +110,11 @@ v86.eax = 0xe801; v86int(); if (!(V86_CY(v86.efl))) { - bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + v = ((v86.ecx & 0xffff) + + ((int64_t)(v86.edx & 0xffff) * 64)) * 1024; + if (v > 0x40000000) + v = 0x40000000; + bios_extmem = v; } } if (bios_extmem == 0) { @@ -121,8 +126,11 @@ } /* Set memtop to actual top of memory */ - memtop = memtop_copyin = 0x100000 + bios_extmem; - + memtop = memtop_copyin = 0x100000 + bios_extmem; /* XXX ignored */ + memtop = memtop_copyin = 64 * 1024 * 1024; + high_heap_size = HEAP_MIN; + high_heap_base = memtop - HEAP_MIN; + /* * If we have extended memory and did not find a suitable heap * region in the SMAP, use the last 3MB of 'extended' memory as a Index: conf/files =================================================================== --- conf/files (revision 272526) +++ conf/files (working copy) @@ -1439,6 +1439,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 ichiic +dev/ichiic/ig4_pci.c optional ichiic pci dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida Index: dev/atkbdc/atkbd.c =================================================================== --- dev/atkbdc/atkbd.c (revision 272526) +++ dev/atkbdc/atkbd.c (working copy) @@ -431,7 +431,7 @@ } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY; - if (KBD_HAS_DEVICE(kbd) + if (!KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) { kbd_unregister(kbd); @@ -443,6 +443,7 @@ delay[0] = kbd->kb_delay1; delay[1] = kbd->kb_delay2; atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); + KBD_FOUND_DEVICE(kbd); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { @@ -1200,7 +1201,6 @@ */ int err; int c; - int m; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ @@ -1214,11 +1214,9 @@ empty_both_buffers(kbdc, 100); /* save the current keyboard controller command byte */ - m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ - kbdc_set_device_mask(kbdc, m); kbdc_lock(kbdc, FALSE); return ENXIO; } @@ -1243,15 +1241,11 @@ * to the system later. It is NOT recommended to hot-plug * the AT keyboard, but many people do so... */ - kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); setup_kbd_port(kbdc, TRUE, TRUE); #if 0 - if (err == 0) { - kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); - } else { + if (err) { /* try to restore the command byte as before */ - set_controller_command_byte(kbdc, 0xff, c); - kbdc_set_device_mask(kbdc, m); + set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, c); } #endif @@ -1299,6 +1293,36 @@ return EIO; } + codeset = -1; + + /* 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, KBD_KBD_CONTROL_BITS, c); + kbdc_lock(kbdc, FALSE); + if (bootverbose) + printf("atkbd: failed to reset the keyboard.\n"); + 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 @@ -1305,7 +1329,6 @@ * been set up properly by BIOS and have not been messed up * during the boot process. */ - codeset = -1; if (flags & KB_CONF_ALT_SCANCODESET) /* the user says there is a XT keyboard */ codeset = 1; @@ -1343,34 +1366,6 @@ 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); - kbdc_lock(kbdc, FALSE); - if (bootverbose) - printf("atkbd: failed to reset the keyboard.\n"); - return EIO; - } - /* * Allow us to set the XT_KEYBD flag so that keyboards * such as those on the IBM ThinkPad laptop computers can be used @@ -1387,7 +1382,7 @@ * 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, KBD_KBD_CONTROL_BITS, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; @@ -1402,6 +1397,16 @@ c |= KBD_TRANSLATION; #endif + /* + * Some keyboards require a SETLEDS command to be sent after + * the reset command before they will send keystrokes to us + * (Acer C720). + */ + if (send_kbd_command_and_data(kbdc, KBDC_SET_LEDS, 0) != KBD_ACK) { + printf("atkbd: setleds failed\n"); + } + 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, @@ -1412,7 +1417,9 @@ * This is serious; we are left with the disabled * keyboard intr. */ - set_controller_command_byte(kbdc, 0xff, c); + set_controller_command_byte(kbdc, + 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; @@ -1437,9 +1444,8 @@ c = get_controller_command_byte(kbdc); if ((c == -1) || !set_controller_command_byte(kbdc, - kbdc_get_device_mask(kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT - | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + KBD_KBD_CONTROL_BITS, + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT)) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); splx(s); @@ -1458,8 +1464,7 @@ send_kbd_command(kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ - if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), - c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { + if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, c)) { /* CONTROLLER ERROR */ } #else Index: dev/atkbdc/atkbdc.c =================================================================== --- dev/atkbdc/atkbdc.c (revision 272526) +++ dev/atkbdc/atkbdc.c (working copy) @@ -1115,19 +1115,6 @@ } int -kbdc_get_device_mask(KBDC p) -{ - return kbdcp(p)->command_mask; -} - -void -kbdc_set_device_mask(KBDC p, int mask) -{ - kbdcp(p)->command_mask = - mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS); -} - -int get_controller_command_byte(KBDC p) { if (kbdcp(p)->command_byte != -1) Index: dev/atkbdc/atkbdcreg.h =================================================================== --- dev/atkbdc/atkbdcreg.h (revision 272526) +++ dev/atkbdc/atkbdcreg.h (working copy) @@ -255,9 +255,6 @@ int test_kbd_port(KBDC kbdc); int test_aux_port(KBDC kbdc); -int kbdc_get_device_mask(KBDC kbdc); -void kbdc_set_device_mask(KBDC kbdc, int mask); - int get_controller_command_byte(KBDC kbdc); int set_controller_command_byte(KBDC kbdc, int command, int flag); Index: dev/atkbdc/psm.c =================================================================== --- dev/atkbdc/psm.c (revision 272526) +++ dev/atkbdc/psm.c (working copy) @@ -993,8 +993,7 @@ /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - (command_byte & KBD_KBD_CONTROL_BITS) | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) { /* CONTROLLER ERROR */ disable_aux_dev(sc->kbdc); @@ -1037,8 +1036,7 @@ /* enable the aux port but disable the aux interrupt and the keyboard */ if ((c == -1) || !set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ splx(s); @@ -1088,8 +1086,7 @@ } else { /* restore the keyboard port and disable the aux port */ if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - (c & KBD_KBD_CONTROL_BITS) | + KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ log(LOG_ERR, "psm%d: failed to disable the aux port " @@ -1142,7 +1139,8 @@ #define endprobe(v) do { \ if (bootverbose) \ --verbose; \ - kbdc_set_device_mask(sc->kbdc, mask); \ + set_controller_command_byte(sc->kbdc, \ + KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT); \ kbdc_lock(sc->kbdc, FALSE); \ return (v); \ } while (0) @@ -1154,7 +1152,6 @@ struct psm_softc *sc = device_get_softc(dev); int stat[3]; int command_byte; - int mask; int rid; int i; @@ -1207,7 +1204,6 @@ empty_both_buffers(sc->kbdc, 10); /* save the current command byte; it will be used later */ - mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS; command_byte = get_controller_command_byte(sc->kbdc); if (verbose) printf("psm%d: current command byte:%04x\n", unit, @@ -1224,14 +1220,12 @@ * enabled during this routine */ if (!set_controller_command_byte(sc->kbdc, - KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know how to recover * from this error... */ - restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } @@ -1270,7 +1264,6 @@ recover_from_error(sc->kbdc); if (sc->config & PSM_CONFIG_IGNPORTERROR) break; - restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: the aux port is not functioning (%d).\n", unit, i); @@ -1293,7 +1286,6 @@ */ if (!reset_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); - restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to reset the aux " "device.\n", unit); @@ -1315,7 +1307,6 @@ 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 (verbose) printf("psm%d: failed to enable the aux device.\n", unit); @@ -1337,7 +1328,6 @@ /* 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 (verbose) printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid); @@ -1436,20 +1426,17 @@ /* disable the aux port for now... */ if (!set_controller_command_byte(sc->kbdc, - KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, - (command_byte & KBD_KBD_CONTROL_BITS) | + KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know the proper way to * recover from this error... */ - restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* done */ - kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (0); } @@ -1596,8 +1583,7 @@ /* enable the aux port and temporalily disable the keyboard */ if (command_byte == -1 || !set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ kbdc_lock(sc->kbdc, FALSE); @@ -1649,8 +1635,7 @@ /* disable the aux interrupt and temporalily disable the keyboard */ if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n", @@ -1691,8 +1676,7 @@ } if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - (command_byte & KBD_KBD_CONTROL_BITS) | + KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * CONTROLLER ERROR; @@ -1850,8 +1834,7 @@ s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | + KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ splx(s); @@ -1914,8 +1897,8 @@ /* restore ports and interrupt */ if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { + KBD_AUX_CONTROL_BITS, + c & (KBD_AUX_CONTROL_BITS))) { /* * CONTROLLER ERROR; this is serious, we may have * been left with the inaccessible keyboard and Index: dev/cyapa/cyapa.c =================================================================== --- dev/cyapa/cyapa.c (revision 0) +++ dev/cyapa/cyapa.c (working copy) @@ -0,0 +1,1679 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * It has been ported/changed/improved for the FreeBSD Project + * by Michael Gmelin + * + * 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. + */ +/* + * 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "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 // was 10 + +#define TIME_TO_IDLE (hz * 10) +#define TIME_TO_RESET (hz * 3) + +struct cyapa_fifo { + int rindex; + int windex; + char buf[CYAPA_BUFSIZE]; +}; + +struct cyapa_softc { + device_t dev; + int count; /* >0 if device opened */ + int unit; + 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 poll_flags; + struct proc *cyapa_kthread; + + /* + * PS/2 mouse emulation + */ + short track_x; /* current tracking */ + short track_y; + short track_z; + int track_z_ticks; + int last_move_ticks; + uint16_t track_but; + char track_id; /* first finger id */ + int track_nfingers; + short delta_x; /* accumulation -> report */ + short delta_y; + short delta_z; + short fuzz_x; + short fuzz_y; + short fuzz_z; + short touch_x; /* touch down coordinates */ + short touch_y; + short 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 resolution; /* count/mm */ + int sample_rate; /* samples/sec */ + int zenabled; /* z-axis enabled (mode 1 or 2) */ + int poll_ticks; +}; + +#define CYPOLL_SHUTDOWN 0x0001 + +static void cyapa_poll_thread(void *arg); +static int cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs); +static void cyapa_set_power_mode(struct cyapa_softc *sc, int mode); + +static int fifo_empty(struct cyapa_fifo *fifo); +static size_t fifo_ready(struct cyapa_fifo *fifo); +static char *fifo_read(struct cyapa_fifo *fifo, size_t n); +static char *fifo_write(struct cyapa_fifo *fifo, size_t n); +static uint8_t fifo_read_char(struct cyapa_fifo *fifo); +static void fifo_write_char(struct cyapa_fifo *fifo, uint8_t c); +static size_t fifo_space(struct cyapa_fifo *fifo); +static void fifo_reset(struct cyapa_fifo *fifo); + +static short cyapa_fuzz(short delta, short *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_scroll_wait_ms = 50; +SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_wait_ms, CTLFLAG_RW, + &cyapa_scroll_wait_ms, 0, "Wait N ms before starting to scroll"); +static int cyapa_scroll_stick_ms = 150; +SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_stick_ms, CTLFLAG_RW, + &cyapa_scroll_stick_ms, 0, "Prevent cursor move on single finger for N ms after scroll"); +static int cyapa_rest_ms = 50; +SYSCTL_INT(_debug, OID_AUTO, cyapa_rest_ms, CTLFLAG_RW, + &cyapa_rest_ms, 0, "ms to detect resting cursor"); +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); +} + +/* + * Notify if possible receive data ready. Must be called + * without the lock held to avoid deadlocking in selinfo. + */ +static +void +cyapa_notify(struct cyapa_softc *sc) +{ + if (sc->data_signal || !fifo_empty(&sc->rfifo)) { + KNOTE_UNLOCKED(&sc->selinfo.si_note, 0); + if (sc->blocked || sc->isselect) { + cyapa_lock(sc); + if (sc->blocked) { + sc->blocked = 0; + wakeup(&sc->blocked); + } + if (sc->isselect) { + sc->isselect = 0; + selwakeup(&sc->selinfo); + } + cyapa_unlock(sc); + } + } +} + +/* + * 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 = 2 * 10; + 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; + } + tsleep(&error, 0, "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 void cyapa_identify(driver_t *driver, device_t parent); +static int cyapa_probe(device_t); +static int cyapa_attach(device_t); +static int cyapa_detach(device_t); + +static devclass_t cyapa_devclass; + +static device_method_t cyapa_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, cyapa_identify), + 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_close_t cyapaclose; +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_close = cyapaclose, + .d_ioctl = cyapaioctl, + .d_read = cyaparead, + .d_write = cyapawrite, + .d_kqfilter = cyapakqfilter, + .d_poll = cyapapoll, +}; + +static void +cyapa_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, "cyapa", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "cyapa", -1); +} + +static int +cyapa_probe(device_t dev) +{ + device_t bus; + struct cyapa_cap cap; + int unit; + int addr; + unsigned char* addr_ptr; + int error; + int dummy = 0; + + bus = device_get_parent(dev); /* smbus */ + + if (!bus) + return (ENXIO); + + addr_ptr = device_get_ivars(dev); + + if (!addr_ptr) { + printf("No address ptr set\n"); + return (ENXIO); + } + + addr = *addr_ptr; + + /* + * 0x67 - cypress trackpad on the acer c720 (other devices might use other ids). + */ + if (addr != 0x067) { + printf("cyapa_probe called on unknown I2C device: %i\n", addr); + return (ENXIO); + } + + unit = device_get_unit(dev); + tsleep(&dummy, 0, "cyastab", hz); + error = init_device(dev, &cap, addr, 1); + if (error) + 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_softc *)device_get_softc(dev); + struct cyapa_cap cap; + unsigned char* addr_ptr; + int unit; + int addr; + + if (!sc) + return ENOMEM; + + bzero(sc, sizeof(struct cyapa_softc *)); + + mtx_init(&sc->mutex, "cyapa", NULL, MTX_DEF); + sc->reporting_mode = 1; + + unit = device_get_unit(dev); + addr_ptr = device_get_ivars(dev); + + if (!addr_ptr) { + printf("No address ptr set\n"); + return (ENXIO); + } + + addr = *addr_ptr; + + if (init_device(dev, &cap, addr, 0)) + return ENXIO; + + sc->dev = dev; + sc->unit = unit; + sc->addr = addr; + + sc->devnode = make_dev(&cyapa_cdevsw, unit, + UID_ROOT, GID_WHEEL, 0600, "cyapa%d", unit); + + sc->devnode->si_drv1 = sc; + knlist_init_mtx(&sc->selinfo.si_note, NULL); + + 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); + + /* + * Setup input event tracking + */ + cyapa_set_power_mode(sc, CMD_POWER_MODE_IDLE); + + /* + * Start the polling thread. + */ + kproc_create(cyapa_poll_thread, sc, + &sc->cyapa_kthread, 0, 0, "cyapa-poll"); + + return (0); +} + +static int +cyapa_detach(device_t dev) +{ + struct cyapa_softc *sc = (struct cyapa_softc *)device_get_softc(dev); + + /* + * Cleanup our poller thread + */ + atomic_set_int(&sc->poll_flags, CYPOLL_SHUTDOWN); + while (sc->cyapa_kthread) { + wakeup(&sc->poll_flags); + tsleep(&sc->cyapa_kthread, 0, "cyapadet", hz); + } + + if (sc->devnode) + 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_softc *sc = dev->si_drv1; + + if (sc == NULL) + return (ENXIO); + + if (sc->count != 0) + return (EBUSY); + + sc->count++; + + return (0); +} + +static int +cyapaclose(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct cyapa_softc *sc = dev->si_drv1; + + if (sc == NULL) + return (ENXIO); + + if (sc->count == 0) + /* This is not supposed to happen. */ + return (0); + + sc->count--; + + return (0); +} + +static int +cyaparead(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct cyapa_softc *sc = dev->si_drv1; + int error; + int didread; + size_t n; + + /* + * If buffer is empty, load a new event if it is ready + */ + cyapa_lock(sc); +again: + if (fifo_empty(&sc->rfifo) && + (sc->data_signal || sc->delta_x || sc->delta_y || + sc->track_but != sc->reported_but)) { + uint8_t c0; + uint16_t but; + short delta_x; + short delta_y; + short 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->rfifo, c0); + fifo_write_char(&sc->rfifo, (uint8_t)delta_x); + fifo_write_char(&sc->rfifo, (uint8_t)delta_y); + switch(sc->zenabled) { + case 1: + /* + * Z axis all 8 bits + */ + fifo_write_char(&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->rfifo, c0); + break; + default: + /* basic PS/2 */ + break; + } + cyapa_unlock(sc); + cyapa_notify(sc); + cyapa_lock(sc); + } + + /* + * Blocking / Non-blocking + */ + error = 0; + didread = (uio->uio_resid == 0); + + while ((ioflag & IO_NDELAY) == 0 && fifo_empty(&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->rfifo)) > 0) { + if (n > uio->uio_resid) + n = uio->uio_resid; + cyapa_unlock(sc); + error = uiomove(fifo_read(&sc->rfifo, 0), n, uio); + cyapa_lock(sc); + if (error) + break; + fifo_read(&sc->rfifo, n); + didread = 1; + } + cyapa_unlock(sc); + + if (error == 0 && didread == 0) { + error = EWOULDBLOCK; + } + return error; +} + +static int +cyapawrite(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct cyapa_softc *sc = dev->si_drv1; + int error; + int cmd_completed; + size_t n; + uint8_t c0; + +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->wfifo)) > 0 && uio->uio_resid) { + if (n > uio->uio_resid) + n = uio->uio_resid; + cyapa_unlock(sc); + error = uiomove(fifo_write(&sc->wfifo, 0), n, uio); + cyapa_lock(sc); + if (error) + break; + fifo_write(&sc->wfifo, n); + } + + /* + * Handle commands + */ + cmd_completed = (fifo_ready(&sc->wfifo) != 0); + while (fifo_ready(&sc->wfifo) && cmd_completed && error == 0) { + if (sc->ps2_cmd == 0) + sc->ps2_cmd = fifo_read_char(&sc->wfifo); + switch(sc->ps2_cmd) { + case 0xE6: + /* + * SET SCALING 1:1 + */ + sc->scaling_mode = 0; + fifo_write_char(&sc->rfifo, 0xFA); + break; + case 0xE7: + /* + * SET SCALING 2:1 + */ + sc->scaling_mode = 1; + fifo_write_char(&sc->rfifo, 0xFA); + break; + case 0xE8: + /* + * SET RESOLUTION +1 byte + */ + if (sc->ps2_acked == 0) { + sc->ps2_acked = 1; + fifo_write_char(&sc->rfifo, 0xFA); + } + if (fifo_ready(&sc->wfifo) == 0) { + cmd_completed = 0; + break; + } + sc->resolution = fifo_read_char(&sc->wfifo); + fifo_write_char(&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->rfifo, 0xFA); + fifo_write_char(&sc->rfifo, c0); + fifo_write_char(&sc->rfifo, 0x00); + fifo_write_char(&sc->rfifo, 100); + break; + case 0xEA: + /* + * Set stream mode and reset movement counters + */ + sc->remote_mode = 0; + fifo_write_char(&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->rfifo, 0xFA); + sc->data_signal = 1; + break; + case 0xEC: + /* + * Reset Wrap Mode (ignored) + */ + fifo_write_char(&sc->rfifo, 0xFA); + break; + case 0xEE: + /* + * Set Wrap Mode (ignored) + */ + fifo_write_char(&sc->rfifo, 0xFA); + break; + case 0xF0: + /* + * Set Remote Mode + */ + sc->remote_mode = 1; + fifo_write_char(&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->rfifo, 0xFA); + switch(sc->zenabled) { + case 1: + fifo_write_char(&sc->rfifo, 0x03); + break; + case 2: + fifo_write_char(&sc->rfifo, 0x04); + break; + default: + fifo_write_char(&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->rfifo, 0xFA); + } + if (fifo_ready(&sc->wfifo) == 0) { + cmd_completed = 0; + break; + } + sc->sample_rate = fifo_read_char(&sc->wfifo); + fifo_write_char(&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->sample_rate == 200) + sc->zenabled = -1; + else if (sc->zenabled == -1 && sc->sample_rate == 100) + sc->zenabled = -2; + else if (sc->zenabled == -1 && sc->sample_rate == 200) + sc->zenabled = -3; + else if (sc->zenabled == -2 && sc->sample_rate == 80) + sc->zenabled = 1; /* z-axis mode */ + else if (sc->zenabled == -3 && sc->sample_rate == 80) + sc->zenabled = 2; /* z-axis+but4/5 */ + break; + case 0xF4: + /* + * Enable data reporting. Only effects stream mode. + */ + fifo_write_char(&sc->rfifo, 0xFA); + sc->reporting_mode = 1; + break; + case 0xF5: + /* + * Disable data reporting. Only effects stream mode. + */ + fifo_write_char(&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->rfifo, 0xFA); + sc->sample_rate = 100; + sc->resolution = 4; + sc->scaling_mode = 0; + sc->reporting_mode = 0; + 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->rfifo, 0xFA); + sc->data_signal = 1; + break; + case 0xFF: + /* + * RESET + */ + fifo_reset(&sc->rfifo); /* should we do this? */ + fifo_reset(&sc->wfifo); /* should we do this? */ + fifo_write_char(&sc->rfifo, 0xFA); + sc->delta_x = 0; + sc->delta_y = 0; + sc->delta_z = 0; + sc->zenabled = 0; + break; + default: + printf("unknown command %02x\n", sc->ps2_cmd); + break; + } + if (cmd_completed) { + sc->ps2_cmd = 0; + sc->ps2_acked = 0; + } + cyapa_unlock(sc); + cyapa_notify(sc); + cyapa_lock(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 = dev->si_drv1; + struct knlist *knlist; + + 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 = dev->si_drv1; + int revents = 0; + + cyapa_lock(sc); + if (events & (POLLIN|POLLRDNORM)) { + if (sc->data_signal || !fifo_empty(&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 cyapa_softc *)kn->kn_hook; + struct knlist *knlist; + + knlist = &sc->selinfo.si_note; + knlist_remove(knlist, kn, 0); +} + +static int +cyapafilt(struct knote *kn, long hint) +{ + struct cyapa_softc *sc = (struct cyapa_softc *)kn->kn_hook; + int ready; + + cyapa_lock(sc); + if (fifo_ready(&sc->rfifo) || sc->data_signal) + ready = 1; + else + ready = 0; + 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 = dev->si_drv1; + device_t bus; /* smbbus */ + void *s = NULL; + int error; + + if (sc == NULL) + return (ENXIO); + if (s == NULL) + return (EINVAL); + + /* + * NOTE: smbus_*() functions automatically recurse the parent to + * get to the actual device driver. + */ + bus = device_get_parent(sc->dev); /* smbus */ + + /* Allocate the bus. */ + if ((error = smbus_request_bus(bus, sc->dev, + (fflag & O_NONBLOCK) ? + SMB_DONTWAIT : (SMB_WAIT | SMB_INTR)))) + return (error); + + switch (cmd) { + default: + error = ENOTTY; + break; + } + + smbus_release_bus(bus, sc->dev); + + return (error); +} + +/* + * MAJOR SUPPORT FUNCTIONS + */ +static +void +cyapa_poll_thread(void *arg) +{ + struct cyapa_softc *sc = arg; + struct cyapa_regs regs; + device_t bus; /* smbbus */ + int error; + int freq = cyapa_norm_freq; + int isidle = 0; + int pstate = CMD_POWER_MODE_IDLE; + int npstate; + int last_reset = ticks; + + bus = device_get_parent(sc->dev); + + while ((sc->poll_flags & CYPOLL_SHUTDOWN) == 0) { + 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 *)®s, sizeof(regs), NULL); + if (error == 0) { + isidle = cyapa_raw_input(sc, ®s); + } + + /* + * 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); + } + tsleep(&sc->poll_flags, 0, "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; + } + } + } + } + sc->cyapa_kthread = NULL; + wakeup(&sc->cyapa_kthread); +} + +static +int +cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs) +{ + int nfingers; + int afingers; /* actual fingers after culling */ + int i; + int j; + int k; + int isidle; + int thumbarea_begin; + short x; + short y; + short z; + short newfinger; + uint16_t but; /* high bits used for simulated but4/but5 */ + + thumbarea_begin = sc->cap_resy - ((sc->cap_resy * cyapa_thumbarea_percent) / 100); + + /* + * 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 + ); + } + + int 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; + sc->track_nfingers = afingers; + + + /* + * Lookup and track finger indexes in the touch[] array. + */ + if (afingers == 0) { + 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_ms * cyapa_norm_freq) / 1000) { + 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_ms * cyapa_norm_freq) / 1000) { + if (sc->track_z == -1) { + sc->delta_z = 0; + } + + z = (CYAPA_TOUCH_Y(regs, 0) + CYAPA_TOUCH_Y(regs, 1)) >> 1; + sc->delta_z += z / ZSCALE - sc->track_z; + 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); + if (sc->track_x != -1 && sc->track_y < thumbarea_begin) { + 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; + + /*if (CYAPA_TOUCH_P(regs, i) > 35 && abs(sc->delta_x) < 10 && abs(sc->delta_y) < 10) { + sc->delta_x = sc->delta_y = 0; + } else { + sc->last_move_ticks = sc->poll_ticks; + }*/ + } + + /* + * Select finger (L = 2/3x, M = 1/3u, R = 1/3d) + */ + if (regs->fngr & CYAPA_FNGR_LEFT) { + if (sc->track_but) { + but = sc->track_but; + } else if (afingers == 1) { + if (sc->track_x < sc->cap_resx * 2 / 3) + but = CYAPA_FNGR_LEFT; + else if (sc->track_y < sc->cap_resy / 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_unlock(sc); + + cyapa_notify(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_fifo *fifo) +{ + 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_fifo *fifo) +{ + size_t n; + + 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_fifo *fifo, size_t n) +{ + char *ptr; + + 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_fifo *fifo) +{ + uint8_t c; + + 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_fifo *fifo, uint8_t c) +{ + 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_fifo *fifo) +{ + size_t n; + + 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_fifo *fifo, size_t n) +{ + char *ptr; + + ptr = fifo->buf + (fifo->windex & CYAPA_BUFMASK); + fifo->windex += n; + + return(ptr); +} + +static +void +fifo_reset(struct cyapa_fifo *fifo) +{ + fifo->rindex = 0; + fifo->windex = 0; +} + +/* + * Fuzz handling + */ +static +short +cyapa_fuzz(short delta, short *fuzzp) +{ + short 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: dev/cyapa/cyapa.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: dev/cyapa/cyapa.h =================================================================== --- dev/cyapa/cyapa.h (revision 0) +++ dev/cyapa/cyapa.h (working copy) @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ + +#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: dev/cyapa/cyapa.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: dev/ichiic/ig4_iic.c =================================================================== --- dev/ichiic/ig4_iic.c (revision 0) +++ dev/ichiic/ig4_iic.c (working copy) @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ +/* + * Intel 4th generation mobile cpus integrated I2C device, smbus driver. + * + * See ig4_reg.h for datasheet reference and notes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#define TRANS_NORMAL 1 +#define TRANS_PCALL 2 +#define TRANS_BLOCK 3 + +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, CTLTYPE_INT | CTLFLAG_RW, + &ig4_dump, 0, ""); + +/* + * Low-level inline support functions + */ +static __inline +void +reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value) +{ + bus_space_write_4(sc->regs_t, sc->regs_h, reg, value); + bus_space_barrier(sc->regs_t, sc->regs_h, reg, 4, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline +uint32_t +reg_read(ig4iic_softc_t *sc, uint32_t reg) +{ + uint32_t value; + + bus_space_barrier(sc->regs_t, sc->regs_h, reg, 4, + BUS_SPACE_BARRIER_READ); + value = bus_space_read_4(sc->regs_t, sc->regs_h, 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->mutex, 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; + u_int limit; + + error = SMB_ETIMEOUT; + /* XXX ticks are not very precise, we should fix that */ + count = ticks; + limit = 5; /* was 3 */ /* (25000 / tick) + 0.5; */ + + 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 = ticks; + } + } + + /* + * Stop if we've run out of time. + */ + if (ticks - count > limit) + 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->mutex, PZERO, "i2cwait", 10); /*(hz + 99) / 100);*/ + } else { + DELAY(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; + + mtx_lock(&sc->mutex); + + v = reg_read(sc, IG4_REG_COMP_TYPE); + printf("type %08x", v); + v = reg_read(sc, IG4_REG_COMP_PARAM1); + printf(" params %08x", v); + v = reg_read(sc, IG4_REG_GENERAL); + printf(" general %08x", v); + if ((v & IG4_GENERAL_SWMODE) == 0) { + v |= IG4_GENERAL_SWMODE; + reg_write(sc, IG4_REG_GENERAL, v); + v = reg_read(sc, IG4_REG_GENERAL); + printf(" (updated %08x)", v); + } + + v = reg_read(sc, IG4_REG_SW_LTR_VALUE); + printf(" swltr %08x", v); + v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); + printf(" autoltr %08x", v); + + v = reg_read(sc, IG4_REG_COMP_VER); + printf(" version %08x\n", v); + if (v != IG4_COMP_VER) { + error = ENXIO; + goto done; + } +#if 1 + v = reg_read(sc, IG4_REG_SS_SCL_HCNT); + printf("SS_SCL_HCNT=%08x", v); + v = reg_read(sc, IG4_REG_SS_SCL_LCNT); + printf(" LCNT=%08x", v); + v = reg_read(sc, IG4_REG_FS_SCL_HCNT); + printf(" FS_SCL_HCNT=%08x", v); + v = reg_read(sc, IG4_REG_FS_SCL_LCNT); + printf(" LCNT=%08x\n", v); + v = reg_read(sc, IG4_REG_SDA_HOLD); + printf("HOLD %08x\n", v); + + 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); +#endif + /* + * 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. + */ + 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); + 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->mutex); + error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ig4iic_intr, sc, &sc->intr_handle); + mtx_lock(&sc->mutex); + if (error) { + device_printf(sc->dev, + "Unable to setup irq: error %d\n", error); + goto done; + } + + /* Attach us to the smbus */ + mtx_unlock(&sc->mutex); + error = bus_generic_attach(sc->dev); + mtx_lock(&sc->mutex); + if (error) { + device_printf(sc->dev, + "failed to attach child: error %d\n", error); + goto done; + } + sc->generic_attached = 1; + +done: + mtx_unlock(&sc->mutex); + return error; +} + +int +ig4iic_detach(ig4iic_softc_t *sc) +{ + int error; + + mtx_lock(&sc->mutex); + + reg_write(sc, IG4_REG_INTR_MASK, 0); + reg_read(sc, IG4_REG_CLR_INTR); + set_controller(sc, 0); + + if (sc->generic_attached) { + error = bus_generic_detach(sc->dev); + if (error) + goto done; + sc->generic_attached = 0; + } + if (sc->smb) { + device_delete_child(sc->dev, sc->smb); + sc->smb = NULL; + } + if (sc->intr_handle) { + bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); + sc->intr_handle = NULL; + } + + error = 0; +done: + mtx_unlock(&sc->mutex); + return error; +} + +int +ig4iic_smb_callback(device_t dev, int index, void *data) +{ + ig4iic_softc_t *sc = device_get_softc(dev); + int error; + + mtx_lock(&sc->mutex); + + switch (index) { + case SMB_REQUEST_BUS: + error = 0; + break; + case SMB_RELEASE_BUS: + error = 0; + break; + default: + error = SMB_EABORT; + break; + } + + mtx_unlock(&sc->mutex); + + 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) +{ + ig4iic_softc_t *sc = device_get_softc(dev); + int error; + + mtx_lock(&sc->mutex); + + switch (how) { + case SMB_QREAD: + error = SMB_ENOTSUPP; + break; + case SMB_QWRITE: + error = SMB_ENOTSUPP; + break; + default: + error = SMB_ENOTSUPP; + break; + } + mtx_unlock(&sc->mutex); + + return error; +} + +/* + * 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; + + mtx_lock(&sc->mutex); + + 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->mutex); + 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; + + mtx_lock(&sc->mutex); + + 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->mutex); + 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; + + mtx_lock(&sc->mutex); + + set_slave_addr(sc, slave, 0); + error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT, + &byte, 1, NULL, 0, NULL); + + mtx_unlock(&sc->mutex); + 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; + + mtx_lock(&sc->mutex); + + 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->mutex); + 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; + + mtx_lock(&sc->mutex); + + set_slave_addr(sc, slave, 0); + error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT, + NULL, 0, byte, 1, NULL); + + mtx_unlock(&sc->mutex); + 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; + + mtx_lock(&sc->mutex); + + 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->mutex); + 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; + + mtx_lock(&sc->mutex); + + 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->mutex); + 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; + + mtx_lock(&sc->mutex); + + set_slave_addr(sc, slave, 0); + error = smb_transaction(sc, cmd, 0, + buf, wcount, NULL, 0, NULL); + + mtx_unlock(&sc->mutex); + 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; + + mtx_lock(&sc->mutex); + + set_slave_addr(sc, slave, 0); + error = smb_transaction(sc, cmd, 0, + NULL, 0, buf, rcount, &rcount); + *countp_char = rcount; + + mtx_unlock(&sc->mutex); + 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; + + mtx_lock(&sc->mutex); + + set_slave_addr(sc, slave, op); + error = smb_transaction(sc, cmd, op, + wbuf, wcount, rbuf, rcount, actualp); + + mtx_unlock(&sc->mutex); + return error; +} + +/* + * Interrupt Operation + */ +static +void +ig4iic_intr(void *cookie) +{ + ig4iic_softc_t *sc = cookie; + uint32_t status; + + mtx_lock(&sc->mutex); +/* 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->mutex); +} + +#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: dev/ichiic/ig4_iic.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: dev/ichiic/ig4_pci.c =================================================================== --- dev/ichiic/ig4_pci.c (revision 0) +++ dev/ichiic/ig4_pci.c (working copy) @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ +/* + * Intel 4th generation mobile cpus integrated I2C device, smbus driver. + * + * See ig4_reg.h for datasheet reference and notes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "smbus_if.h" + +#include +#include + +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 msi_enable = 1; + int error; + + bzero(sc, sizeof(*sc)); + + mtx_init(&sc->mutex, device_get_nameunit(dev), "ig4iic", MTX_DEF); + + 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 (msi_enable && 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->regs_t = rman_get_bustag(sc->regs_res); + sc->regs_h = rman_get_bushandle(sc->regs_res); + 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; + } + sc->regs_t = 0; + sc->regs_h = 0; + mtx_destroy(&sc->mutex); + + 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: dev/ichiic/ig4_pci.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: dev/ichiic/ig4_reg.h =================================================================== --- dev/ichiic/ig4_reg.h (revision 0) +++ dev/ichiic/ig4_reg.h (working copy) @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ +/* + * Intel 4th 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: 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: dev/ichiic/ig4_var.h =================================================================== --- dev/ichiic/ig4_var.h (revision 0) +++ dev/ichiic/ig4_var.h (working copy) @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ + +#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; + device_t smb; + struct resource *regs_res; + int regs_rid; + bus_space_tag_t regs_t; + bus_space_handle_t regs_h; + 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 generic_attached : 1; + int use_10bit : 1; + int slave_valid : 1; + int read_started : 1; + int write_started : 1; + struct mtx mutex; +}; + +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: 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: dev/isl/isl.c =================================================================== --- dev/isl/isl.c (revision 0) +++ dev/isl/isl.c (working copy) @@ -0,0 +1,426 @@ +/*- + * Copyright (c) 2014 Michael Gmelin + * 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 +__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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ + +#include +#include +#include "isl.h" + +#include "smbus_if.h" +#include "bus_if.h" +#include "device_if.h" + +#define ISL_BUFSIZE 128 /* power of 2 */ +#define ISL_BUFMASK (ISL_BUFSIZE - 1) + +#define ZSCALE 10 + +#define HZ_BASE 100 +#define TIME_TO_IDLE (HZ_BASE * 10) +#define TIME_TO_RESET (HZ_BASE * 3) + +#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 count; /* >0 if device opened */ + int unit; + int addr; + struct cdev* devnode; + struct selinfo selinfo; + struct mtx mutex; + + int poll_flags; + struct proc *isl_kthread; + struct sysctl_ctx_list *sysctl_ctx; + struct sysctl_oid *sysctl_tree; +}; + +static int isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask); // returns < 0 on problem + +static int isl_debug = 0; +SYSCTL_INT(_debug, OID_AUTO, isl_debug, CTLFLAG_RW, + &isl_debug, 0, ""); +static int isl_reset = 0; +SYSCTL_INT(_debug, OID_AUTO, isl_reset, CTLFLAG_RW, + &isl_reset, 0, ""); + +/* + * 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); ///1000 + 1); + +done: + if (error) + device_printf(dev, "Unable to initialize\n"); + return error; +} + +static void isl_identify(driver_t *driver, device_t parent); +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_identify, isl_identify), + 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 struct cdevsw isl_cdevsw = { + .d_version = D_VERSION, +// .d_close = islclose, +// .d_ioctl = islioctl, +// .d_read = islread, +// .d_write = islwrite, +// .d_kqfilter = islkqfilter, +// .d_poll = islpoll, +}; + +static void +isl_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, "isl", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "isl", -1); +} + +static int +isl_probe(device_t dev) +{ + device_t bus; + int unit; + int addr; + unsigned char* addr_ptr; + int error; + int dummy = 0; + + bus = device_get_parent(dev); /* smbus */ + + if (!bus) + return (ENXIO); + + addr_ptr = device_get_ivars(dev); + + if (!addr_ptr) { + printf("No address ptr set, parent %s\n", device_get_name(bus) ? device_get_name(bus) : "unknown"); + return (ENXIO); + } + + addr = *addr_ptr; + + + /* + * Only match against specific addresses to avoid blowing up + * other I2C devices (?). At least for now. + * + * 0x44 - isl ambient light sensor on the acer c720. + */ + if (addr != 0x44) { + printf("isl_probe called on unknown I2C device: %i\n", addr); + return (ENXIO); + } + + unit = device_get_unit(dev); + tsleep(&dummy, 0, "cyastab", hz); + //addr = unit & 0x3FF; + 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 isl_softc *)device_get_softc(dev); + unsigned char* addr_ptr; + int unit; + int addr; + + if (!sc) + return ENOMEM; + + bzero(sc, sizeof(struct isl_softc *)); + + mtx_init(&sc->mutex, "isl", NULL, MTX_DEF); + + unit = device_get_unit(dev); + addr_ptr = device_get_ivars(dev); + + if (!addr_ptr) { + printf("No address ptr set\n"); + return (ENXIO); + } + + addr = *addr_ptr; + + if (init_device(dev, addr, 0)) + return ENXIO; + + sc->dev = dev; + sc->unit = unit; + sc->addr = addr; + + sc->sysctl_ctx = device_get_sysctl_ctx(dev); + sc->sysctl_tree = device_get_sysctl_tree(dev); + + if (isl_read_sensor(dev, addr, CMD1_MASK_ALS_ONCE) >= 0) { + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "als", CTLTYPE_INT | CTLFLAG_RD, + sc, ISL_METHOD_ALS, isl_sysctl, "I", + "Current ALS sensor read-out"); + } + + if (isl_read_sensor(dev, addr, CMD1_MASK_IR_ONCE) >= 0) { + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "ir", CTLTYPE_INT | CTLFLAG_RD, + sc, ISL_METHOD_IR, isl_sysctl, "I", + "Current IR sensor read-out"); + } + + if (isl_read_sensor(dev, addr, CMD1_MASK_PROX_ONCE) >= 0) { + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "prox", CTLTYPE_INT | CTLFLAG_RD, + sc, ISL_METHOD_PROX, isl_sysctl, "I", + "Current proximity sensor read-out"); + } + + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "resolution", CTLTYPE_INT | CTLFLAG_RD, + sc, ISL_METHOD_RESOLUTION, isl_sysctl, "I", + "Current proximity sensor resolution"); + + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "range", CTLTYPE_INT | CTLFLAG_RD, + sc, ISL_METHOD_RANGE, isl_sysctl, "I", + "Current proximity sensor range"); + + sc->devnode = make_dev(&isl_cdevsw, unit, + UID_ROOT, GID_WHEEL, 0600, "isl%d", unit); + + sc->devnode->si_drv1 = sc; + knlist_init_mtx(&sc->selinfo.si_note, NULL); + + return (0); +} + +static int +isl_detach(device_t dev) +{ + struct isl_softc *sc = (struct isl_softc *)device_get_softc(dev); + + if (sc->devnode) + 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); +} + +static int +isl_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct isl_softc *sc = (struct isl_softc *)oidp->oid_arg1; + device_t bus; + uint8_t rbyte; + int arg = -1; + static int resolutions[] = { 16, 12, 8, 4}; + static int ranges[] = { 1000, 4000, 16000, 64000}; + int resolution; + int range; + + 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)) { + 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; + } + + 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; + device_printf(dev, "Sending command %d\n", cmd); + 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("ilsconv", 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 << 8; + + 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; + + 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: dev/isl/isl.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: dev/isl/isl.h =================================================================== --- dev/isl/isl.h (revision 0) +++ dev/isl/isl.h (working copy) @@ -0,0 +1,41 @@ +/* + * Michael Gmelin + */ + +#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: dev/isl/isl.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: dev/smbus/smb.c =================================================================== --- dev/smbus/smb.c (revision 272526) +++ dev/smbus/smb.c (working copy) @@ -50,6 +50,7 @@ struct smb_softc { device_t sc_dev; int sc_count; /* >0 if device opened */ + int sc_unit; struct cdev *sc_devnode; struct mtx sc_lock; }; @@ -106,7 +107,8 @@ { device_set_desc(dev, "SMBus generic I/O"); - return (0); + /* Allow other subclasses to override this driver. */ + return (BUS_PROBE_GENERIC); } static int @@ -113,10 +115,20 @@ 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_unit = unit; - 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)); +/* if (unit & 0x0400) { + sc->sc_devnode = make_dev(&smb_cdevsw, unit, + UID_ROOT, GID_WHEEL, 0600, + "smb%d-%02x", unit >> 11, unit & 1023); + } else { */ + 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 +186,18 @@ 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 = sc->sc_unit; + if (unit & 0x0400) { + s->slave = unit & 1023; + } + parent = device_get_parent(smbdev); /* Make sure that LSB bit is cleared. */ @@ -208,59 +228,58 @@ 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 (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 (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 (s->rbuf && s->rcount >= 2) { + char buf[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) + s->wcount = 0; + 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) @@ -267,18 +286,39 @@ 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) + s->rcount = 0; + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + error = smbus_bread(parent, s->slave, s->cmd, &bcount, buf); + error = smbus_error(error); + 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->rcount = 0; + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + if (s->wcount < 0) + s->wcount = 0; + if (s->wcount > SMB_MAXBLOCKSIZE) + s->wcount = SMB_MAXBLOCKSIZE; + if (s->wcount) + error = copyin(s->wbuf, buf, s->wcount); + if (error) + break; + error = smbus_trans(parent, s->slave, s->cmd, s->op, + buf, s->wcount, buf, s->rcount, &s->rcount); + error = smbus_error(error); + if (error == 0) + error = copyout(buf, s->rbuf, s->rcount); + break; + default: error = ENOTTY; } Index: dev/smbus/smb.h =================================================================== --- dev/smbus/smb.h (revision 272526) +++ dev/smbus/smb.h (working copy) @@ -32,27 +32,33 @@ #include 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; /* max 255 bytes */ + char *rbuf; /* use rdata if NULL */ + int rcount; /* max 255 bytes */ }; /* * SMBus spec 2.0 says block transfers may be at most 32 bytes. + * We usse 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: dev/smbus/smbconf.h =================================================================== --- dev/smbus/smbconf.h (revision 272526) +++ dev/smbus/smbconf.h (working copy) @@ -68,6 +68,25 @@ #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 */ @@ -104,6 +123,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: dev/smbus/smbus.c =================================================================== --- dev/smbus/smbus.c (revision 272526) +++ dev/smbus/smbus.c (working copy) @@ -38,6 +38,10 @@ #include #include +#include "smbus_if.h" +#include "bus_if.h" + + /* * Autoconfiguration and support routines for System Management bus */ @@ -49,6 +53,10 @@ static int smbus_attach(device_t); static int smbus_detach(device_t); +static device_t smbus_add_child(device_t parent, u_int order, + const char *name, int unit); +static void smbus_probe_device(device_t dev, u_char* addr); + static device_method_t smbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, smbus_probe), @@ -56,7 +64,8 @@ DEVMETHOD(device_detach, smbus_detach), /* bus interface */ - DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_add_child, smbus_add_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD_END }; @@ -87,9 +96,16 @@ 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); + + device_add_child(dev, NULL, -1); /* XXX: device_get_unit(dev)); */ + for (addr = 16; addr < 112; ++addr) { + sc->addrs[addr] = addr; + smbus_probe_device(dev, &sc->addrs[addr]); + } + /*bus_generic_probe(dev);*/ bus_generic_attach(dev); return (0); @@ -114,4 +130,35 @@ { } +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) { + device_printf(dev, "Probed address 0x%02x\n", *addr); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, addr); + } +} + +static device_t +smbus_add_child(device_t parent, u_int order, const char *name, int unit) +{ + device_t child; + + device_printf(parent, "smbus_add_child unit %d at %d\n", unit, order); + child = device_add_child_ordered(parent, order, NULL, unit); + device_probe_and_attach(child); + + return child; +} + MODULE_VERSION(smbus, SMBUS_MODVER); Index: dev/smbus/smbus.h =================================================================== --- dev/smbus/smbus.h (revision 272526) +++ dev/smbus/smbus.h (working copy) @@ -32,6 +32,7 @@ struct smbus_softc { device_t owner; /* smbus owner device structure */ struct mtx lock; + unsigned char addrs[112]; }; void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err); Index: dev/smbus/smbus_if.m =================================================================== --- dev/smbus/smbus_if.m (revision 272526) +++ 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: dev/usb/controller/ehci_pci.c =================================================================== --- dev/usb/controller/ehci_pci.c (revision 272526) +++ dev/usb/controller/ehci_pci.c (working copy) @@ -162,6 +162,8 @@ return ("Intel Lynx Point USB 2.0 controller USB-A"); case 0x8c2d8086: return ("Intel Lynx Point USB 2.0 controller USB-B"); + case 0x9c268086: + return ("Intel Lynx Point LP USB 2.0 controller USB"); case 0x00e01033: return ("NEC uPD 720100 USB 2.0 controller"); Index: modules/i2c/Makefile =================================================================== --- modules/i2c/Makefile (revision 272526) +++ 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 Index: modules/i2c/controllers/Makefile =================================================================== --- modules/i2c/controllers/Makefile (revision 272526) +++ modules/i2c/controllers/Makefile (working copy) @@ -3,7 +3,7 @@ .if ${MACHINE} == "pc98" SUBDIR = lpbb .else -SUBDIR = alpm amdpm amdsmb ichsmb intpm ismt nfsmb viapm lpbb pcf +SUBDIR = alpm amdpm amdsmb ichiic ichsmb intpm ismt nfsmb viapm lpbb pcf .endif .include Index: modules/i2c/controllers/ichiic/Makefile =================================================================== --- modules/i2c/controllers/ichiic/Makefile (revision 0) +++ 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 Property changes on: modules/i2c/controllers/ichiic/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: modules/i2c/cyapa/Makefile =================================================================== --- modules/i2c/cyapa/Makefile (revision 0) +++ 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 Property changes on: modules/i2c/cyapa/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: modules/i2c/isl/Makefile =================================================================== --- modules/i2c/isl/Makefile (revision 0) +++ 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 Property changes on: modules/i2c/isl/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