summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-03-16 12:44:20 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-03-16 12:44:20 +0000
commitdc2e1a589aa53a62f60136a4eb727fa770d2e02e (patch)
tree53d0723062766eb7a33421dc50a80e154901c133 /drivers
parent83b63cbde17a215fad5303e85a4546e2d31c7613 (diff)
Update ITE 8712 code.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/Config.in13
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/ite_gpio.c428
-rw-r--r--drivers/char/qtronix.c18
-rw-r--r--drivers/i2c/Config.in7
-rw-r--r--drivers/i2c/Makefile12
-rw-r--r--drivers/i2c/i2c-adap-ite.c319
-rw-r--r--drivers/i2c/i2c-algo-ite.c872
-rw-r--r--drivers/i2c/i2c-ite.h117
-rw-r--r--drivers/ide/it8172.c88
-rw-r--r--drivers/sound/ite8172.c1957
11 files changed, 3775 insertions, 57 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 21935083b..fbce24791 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -60,6 +60,16 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
fi
fi
+if [ "$CONFIG_IT8712" = "y" ]; then
+ bool 'Enable Qtronix 990P Keyboard Support' CONFIG_QTRONIX_KEYBOARD
+ if [ "$CONFIG_QTRONIX_KEYBOARD" = "y" ]; then
+ define_bool CONFIG_IT8172_CIR y
+ else
+ bool ' Enable PS2 Keyboard Support' CONFIG_PC_KEYB
+ fi
+ bool 'Enable Smart Card Reader 0 Support ' CONFIG_IT8172_SCR0
+ bool 'Enable Smart Card Reader 1 Support ' CONFIG_IT8172_SCR1
+fi
if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
bool 'DC21285 serial port support' CONFIG_SERIAL_21285
if [ "$CONFIG_SERIAL_21285" = "y" ]; then
@@ -191,4 +201,7 @@ source drivers/char/drm/Config.in
if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
source drivers/char/pcmcia/Config.in
fi
+if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then
+ tristate ' ITE GPIO' CONFIG_ITE_GPIO
+fi
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 830f8a8ec..20d718501 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -168,6 +168,7 @@ endif
obj-$(CONFIG_TOSHIBA) += toshiba.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_INTEL_RNG) += i810_rng.o
+obj-$(CONFIG_ITE_GPIO) += ite_gpio.o
obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
diff --git a/drivers/char/ite_gpio.c b/drivers/char/ite_gpio.c
new file mode 100644
index 000000000..e1bcab574
--- /dev/null
+++ b/drivers/char/ite_gpio.c
@@ -0,0 +1,428 @@
+/*
+ * FILE NAME ite_gpio.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ * API for ITE GPIO device.
+ * Driver for ITE GPIO device.
+ *
+ * Author: MontaVista Software, Inc. <source@mvista.com>
+ * Hai-Pao Fan <haipao@mvista.com>
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+#include <asm/addrspace.h>
+#include <asm/it8172/it8172_int.h>
+#include <linux/sched.h>
+#include <linux/ite_gpio.h>
+
+#define ite_gpio_base 0x14013800
+
+#define ITE_GPADR (*(volatile __u8 *)(0x14013800 + KSEG1))
+#define ITE_GPBDR (*(volatile __u8 *)(0x14013808 + KSEG1))
+#define ITE_GPCDR (*(volatile __u8 *)(0x14013810 + KSEG1))
+#define ITE_GPACR (*(volatile __u16 *)(0x14013802 + KSEG1))
+#define ITE_GPBCR (*(volatile __u16 *)(0x1401380a + KSEG1))
+#define ITE_GPCCR (*(volatile __u16 *)(0x14013812 + KSEG1))
+#define ITE_GPAICR (*(volatile __u16 *)(0x14013804 + KSEG1))
+#define ITE_GPBICR (*(volatile __u16 *)(0x1401380c + KSEG1))
+#define ITE_GPCICR (*(volatile __u16 *)(0x14013814 + KSEG1))
+#define ITE_GPAISR (*(volatile __u8 *)(0x14013806 + KSEG1))
+#define ITE_GPBISR (*(volatile __u8 *)(0x1401380e + KSEG1))
+#define ITE_GPCISR (*(volatile __u8 *)(0x14013816 + KSEG1))
+#define ITE_GCR (*(volatile __u8 *)(0x14013818 + KSEG1))
+
+#define MAX_GPIO_LINE 21
+static int ite_gpio_irq=IT8172_GPIO_IRQ;
+
+static long ite_irq_counter[MAX_GPIO_LINE];
+wait_queue_head_t ite_gpio_wait[MAX_GPIO_LINE];
+static int ite_gpio_irq_pending[MAX_GPIO_LINE];
+
+static int ite_gpio_debug=0;
+#define DEB(x) if (ite_gpio_debug>=1) x
+
+int ite_gpio_in(__u32 device, __u32 mask, volatile __u32 *data)
+{
+ DEB(printk("ite_gpio_in mask=0x%x\n",mask));
+
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ ITE_GPACR = (__u16)mask; /* 0xffff */
+ *data = ITE_GPADR;
+ break;
+ case ITE_GPIO_PORTB:
+ ITE_GPBCR = (__u16)mask; /* 0xffff */
+ *data = ITE_GPBDR;
+ break;
+ case ITE_GPIO_PORTC:
+ ITE_GPCCR = (__u16)mask; /* 0x03ff */
+ *data = ITE_GPCDR;
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+
+int ite_gpio_out(__u32 device, __u32 mask, __u32 data)
+{
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ ITE_GPACR = (__u16)mask; /* 0x5555 */
+ ITE_GPADR = (__u8)data;
+ break;
+ case ITE_GPIO_PORTB:
+ ITE_GPBCR = (__u16)mask; /* 0x5555 */
+ ITE_GPBDR = (__u8)data;
+ break;
+ case ITE_GPIO_PORTC:
+ ITE_GPCCR = (__u16)mask; /* 0x0155 */
+ ITE_GPCDR = (__u8)data;
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int ite_gpio_int_ctrl(__u32 device, __u32 mask, __u32 data)
+{
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ ITE_GPAICR = (ITE_GPAICR & ~mask) | (data & mask);
+ break;
+ case ITE_GPIO_PORTB:
+ ITE_GPBICR = (ITE_GPBICR & ~mask) | (data & mask);
+ break;
+ case ITE_GPIO_PORTC:
+ ITE_GPCICR = (ITE_GPCICR & ~mask) | (data & mask);
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int ite_gpio_in_status(__u32 device, __u32 mask, volatile __u32 *data)
+{
+ int ret=-1;
+
+ if (MAX_GPIO_LINE > *data >= 0)
+ ret=ite_gpio_irq_pending[*data];
+
+ DEB(printk("ite_gpio_in_status %d ret=%d\n",*data, ret));
+
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ *data = ITE_GPAISR & mask;
+ break;
+ case ITE_GPIO_PORTB:
+ *data = ITE_GPBISR & mask;
+ break;
+ case ITE_GPIO_PORTC:
+ *data = ITE_GPCISR & mask;
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+int ite_gpio_out_status(__u32 device, __u32 mask, __u32 data)
+{
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ ITE_GPAISR = (ITE_GPAISR & ~mask) | (data & mask);
+ break;
+ case ITE_GPIO_PORTB:
+ ITE_GPBISR = (ITE_GPBISR & ~mask) | (data & mask);
+ break;
+ case ITE_GPIO_PORTC:
+ ITE_GPCISR = (ITE_GPCISR & ~mask) | (data & mask);
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int ite_gpio_gen_ctrl(__u32 device, __u32 mask, __u32 data)
+{
+ ITE_GCR = (ITE_GCR & ~mask) | (data & mask);
+
+ return 0;
+}
+
+int ite_gpio_int_wait (__u32 device, __u32 mask, __u32 data)
+{
+ int i,line=0, ret=0;
+ unsigned long flags;
+
+ switch (device) {
+ case ITE_GPIO_PORTA:
+ line = data & mask;
+ break;
+ case ITE_GPIO_PORTB:
+ line = (data & mask) <<8;
+ break;
+ case ITE_GPIO_PORTC:
+ line = (data & mask) <<16;
+ break;
+ }
+ for (i=MAX_GPIO_LINE-1; i >= 0; i--) {
+ if ( (line) & (1 << i))
+ break;
+ }
+
+ DEB(printk("wait device=0x%d mask=0x%x data=0x%x index %d\n",
+ device, mask, data, i));
+
+ if (line & ~(1<<i))
+ return -EFAULT;
+
+ if (ite_gpio_irq_pending[i]==1)
+ return -EFAULT;
+
+ save_flags (flags);
+ cli();
+ ite_gpio_irq_pending[i] = 1;
+ ret = interruptible_sleep_on_timeout(&ite_gpio_wait[i], 3*HZ);
+ restore_flags (flags);
+ ite_gpio_irq_pending[i] = 0;
+
+ return ret;
+}
+
+EXPORT_SYMBOL(ite_gpio_in);
+EXPORT_SYMBOL(ite_gpio_out);
+EXPORT_SYMBOL(ite_gpio_int_ctrl);
+EXPORT_SYMBOL(ite_gpio_in_status);
+EXPORT_SYMBOL(ite_gpio_out_status);
+EXPORT_SYMBOL(ite_gpio_gen_ctrl);
+EXPORT_SYMBOL(ite_gpio_int_wait);
+
+static int ite_gpio_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ if (minor != GPIO_MINOR)
+ return -ENODEV;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+
+static int ite_gpio_release(struct inode *inode, struct file *file)
+{
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+
+static int ite_gpio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ static struct ite_gpio_ioctl_data ioctl_data;
+
+ if (copy_from_user(&ioctl_data, (struct ite_gpio_ioctl_data *)arg,
+ sizeof(ioctl_data)))
+ return -EFAULT;
+ if ((ioctl_data.device < ITE_GPIO_PORTA) ||
+ (ioctl_data.device > ITE_GPIO_PORTC) )
+ return -EFAULT;
+
+ switch(cmd) {
+ case ITE_GPIO_IN:
+ if (ite_gpio_in(ioctl_data.device, ioctl_data.mask,
+ &ioctl_data.data))
+ return -EFAULT;
+
+ if (copy_to_user((struct ite_gpio_ioctl_data *)arg,
+ &ioctl_data, sizeof(ioctl_data)))
+ return -EFAULT;
+ break;
+
+ case ITE_GPIO_OUT:
+ return ite_gpio_out(ioctl_data.device,
+ ioctl_data.mask, ioctl_data.data);
+ break;
+
+ case ITE_GPIO_INT_CTRL:
+ return ite_gpio_int_ctrl(ioctl_data.device,
+ ioctl_data.mask, ioctl_data.data);
+ break;
+
+ case ITE_GPIO_IN_STATUS:
+ if (ite_gpio_in_status(ioctl_data.device, ioctl_data.mask,
+ &ioctl_data.data))
+ return -EFAULT;
+ if (copy_to_user((struct ite_gpio_ioctl_data *)arg,
+ &ioctl_data, sizeof(ioctl_data)))
+ return -EFAULT;
+ break;
+
+ case ITE_GPIO_OUT_STATUS:
+ return ite_gpio_out_status(ioctl_data.device,
+ ioctl_data.mask, ioctl_data.data);
+ break;
+
+ case ITE_GPIO_GEN_CTRL:
+ return ite_gpio_gen_ctrl(ioctl_data.device,
+ ioctl_data.mask, ioctl_data.data);
+ break;
+
+ case ITE_GPIO_INT_WAIT:
+ return ite_gpio_int_wait(ioctl_data.device,
+ ioctl_data.mask, ioctl_data.data);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+
+ }
+ return 0;
+}
+
+static void ite_gpio_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ int i,line;
+
+ line = ITE_GPCISR & 0x1f;
+ for (i=4; i >=0; i--) {
+ if ( line & (1 << i)) {
+ ++ite_irq_counter[i+16];
+ ite_gpio_irq_pending[i+16] = 2;
+ wake_up_interruptible(&ite_gpio_wait[i+16]);
+
+DEB(printk("interrupt 0x%x %d\n", &ite_gpio_wait[i+16], i+16));
+
+ ITE_GPCISR = ITE_GPCISR & (1<<i);
+ return;
+ }
+ }
+ line = ITE_GPBISR;
+ for (i=7; i >= 0; i--) {
+ if ( line & (1 << i)) {
+ ++ite_irq_counter[i+8];
+ ite_gpio_irq_pending[i+8] = 2;
+ wake_up_interruptible(&ite_gpio_wait[i+8]);
+
+DEB(printk("interrupt 0x%x %d\n",ITE_GPBISR, i+8));
+
+ ITE_GPBISR = ITE_GPBISR & (1<<i);
+ return;
+ }
+ }
+ line = ITE_GPAISR;
+ for (i=7; i >= 0; i--) {
+ if ( line & (1 << i)) {
+ ++ite_irq_counter[i];
+ ite_gpio_irq_pending[i] = 2;
+ wake_up_interruptible(&ite_gpio_wait[i]);
+
+DEB(printk("interrupt 0x%x %d\n",ITE_GPAISR, i));
+
+ ITE_GPAISR = ITE_GPAISR & (1<<i);
+ return;
+ }
+ }
+}
+
+static struct file_operations ite_gpio_fops =
+{
+ owner: THIS_MODULE,
+ ioctl: ite_gpio_ioctl,
+ open: ite_gpio_open,
+ release: ite_gpio_release,
+};
+
+/* GPIO_MINOR in include/linux/miscdevice.h */
+static struct miscdevice ite_gpio_miscdev =
+{
+ GPIO_MINOR,
+ "ite_gpio",
+ &ite_gpio_fops
+};
+
+int __init ite_gpio_init(void)
+{
+ int i;
+
+ misc_register(&ite_gpio_miscdev);
+
+ if (check_region(ite_gpio_base, 0x1c) < 0 ) {
+ return -ENODEV;
+ } else {
+ request_region(ite_gpio_base, 0x1c, "ITE GPIO");
+ }
+
+ /* initialize registers */
+ ITE_GPACR = 0xffff;
+ ITE_GPBCR = 0xffff;
+ ITE_GPCCR = 0xffff;
+ ITE_GPAICR = 0x00ff;
+ ITE_GPBICR = 0x00ff;
+ ITE_GPCICR = 0x00ff;
+ ITE_GCR = 0;
+
+ for (i = 0; i < MAX_GPIO_LINE; i++) {
+ ite_gpio_irq_pending[i]=0;
+ init_waitqueue_head(&ite_gpio_wait[i]);
+ }
+ if (request_irq(ite_gpio_irq, ite_gpio_irq_handler, SA_SHIRQ, "gpio", 0) < 0)
+ return 0;
+ printk("GPIO at 0x%x (irq = %d)\n", ite_gpio_base, ite_gpio_irq);
+
+ return 0;
+}
+
+void __exit ite_gpio_exit(void)
+{
+ misc_deregister(&ite_gpio_miscdev);
+}
+
+module_init(ite_gpio_init);
+module_exit(ite_gpio_exit);
diff --git a/drivers/char/qtronix.c b/drivers/char/qtronix.c
index c74aabf2b..b560ecbca 100644
--- a/drivers/char/qtronix.c
+++ b/drivers/char/qtronix.c
@@ -6,7 +6,7 @@
*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
- * ppopov@mvista.com or support@mvista.com
+ * ppopov@mvista.com or source@mvista.com
*
*
* The bottom portion of this driver was take from
@@ -353,19 +353,19 @@ spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
static unsigned char handle_kbd_event(void);
-int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
+int kbd_setkeycode(unsigned int scancode, unsigned int keycode)
{
- printk("pckbd_setkeycode scancode %x keycode %x\n", scancode, keycode);
+ printk("kbd_setkeycode scancode %x keycode %x\n", scancode, keycode);
return 0;
}
-int pckbd_getkeycode(unsigned int scancode)
+int kbd_getkeycode(unsigned int scancode)
{
return scancode;
}
-int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+int kbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode)
{
static int prev_scancode = 0;
@@ -406,9 +406,9 @@ int pckbd_translate(unsigned char scancode, unsigned char *keycode,
return 1;
}
-char pckbd_unexpected_up(unsigned char keycode)
+char kbd_unexpected_up(unsigned char keycode)
{
- //printk("pckbd_unexpected_up\n");
+ //printk("kbd_unexpected_up\n");
return 0;
}
@@ -422,12 +422,12 @@ static inline void handle_keyboard_event(unsigned char scancode, int down)
}
-void pckbd_leds(unsigned char leds)
+void kbd_leds(unsigned char leds)
{
}
/* dummy */
-void pckbd_init_hw(void)
+void kbd_init_hw(void)
{
}
diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in
index 405121edc..7e168eeff 100644
--- a/drivers/i2c/Config.in
+++ b/drivers/i2c/Config.in
@@ -20,6 +20,13 @@ if [ "$CONFIG_I2C" != "n" ]; then
dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF
fi
+ if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then
+ dep_tristate 'ITE I2C Algorithm' CONFIG_ITE_I2C_ALGO $CONFIG_I2C
+ if [ "CONFIG_ITE_I2C_ALGO" != "n" ]; then
+ dep_tristate ' ITE I2C Adapter' CONFIG_ITE_I2C_ADAP $CONFIG_ITE_I2C_ALGO
+ fi
+ fi
+
# This is needed for automatic patch generation: sensors code starts here
# This is needed for automatic patch generation: sensors code ends here
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index d25ed14f3..9ed18abc1 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -2,9 +2,10 @@
# Makefile for the kernel i2c bus driver.
#
+MOD_LIST_NAME := I2C_MODULES
O_TARGET := i2c.o
-export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o
+export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o i2c-algo-ite.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
@@ -14,9 +15,18 @@ obj-$(CONFIG_I2C_ELV) += i2c-elv.o
obj-$(CONFIG_I2C_VELLEMAN) += i2c-velleman.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
+obj-$(CONFIG_I2C_ALGO8XX) += i2c-algo-8xx.o
+obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o
+obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o
+obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o
# This is needed for automatic patch generation: sensors code starts here
# This is needed for automatic patch generation: sensors code ends here
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
include $(TOPDIR)/Rules.make
diff --git a/drivers/i2c/i2c-adap-ite.c b/drivers/i2c/i2c-adap-ite.c
new file mode 100644
index 000000000..c6a35dafc
--- /dev/null
+++ b/drivers/i2c/i2c-adap-ite.c
@@ -0,0 +1,319 @@
+/*
+ -------------------------------------------------------------------------
+ i2c-adap-ite.c i2c-hw access for the IIC peripheral on the ITE MIPS system
+ -------------------------------------------------------------------------
+ Hai-Pao Fan, MontaVista Software, Inc.
+ hpfan@mvista.com or source@mvista.com
+
+ Copyright 2001 MontaVista Software Inc.
+
+ ----------------------------------------------------------------------------
+ This file was highly leveraged from i2c-elektor.c, which was created
+ by Simon G. Vogl and Hans Berglund:
+
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ 1998-99 Hans Berglund
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* ------------------------------------------------------------------------- */
+
+/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+ Frodo Looijaard <frodol@dds.nl> */
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-ite.h>
+#include <linux/i2c-adap-ite.h>
+#include "i2c-ite.h"
+
+#define DEFAULT_BASE 0x14014030
+#define ITE_IIC_IO_SIZE 0x40
+#define DEFAULT_IRQ 0
+#define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */
+#define DEFAULT_OWN 0x55
+
+static int base = 0;
+static int irq = 0;
+static int clock = 0;
+static int own = 0;
+
+static int i2c_debug=0;
+static struct iic_ite gpi;
+#if (LINUX_VERSION_CODE < 0x020301)
+static struct wait_queue *iic_wait = NULL;
+#else
+static wait_queue_head_t iic_wait;
+#endif
+static int iic_pending;
+
+/* ----- global defines ----------------------------------------------- */
+#define DEB(x) if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x
+#define DEBE(x) x /* error messages */
+
+
+/* ----- local functions ---------------------------------------------- */
+
+static void iic_ite_setiic(void *data, int ctl, short val)
+{
+ unsigned long j = jiffies + 10;
+
+ DEB3(printk(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff));
+ DEB3({while (jiffies < j) schedule();})
+ outw(val,ctl);
+}
+
+static short iic_ite_getiic(void *data, int ctl)
+{
+ short val;
+
+ val = inw(ctl);
+ DEB3(printk("Read 0x%02x from 0x%x\n",(unsigned short)val, ctl&0xff));
+ return (val);
+}
+
+/* Return our slave address. This is the address
+ * put on the I2C bus when another master on the bus wants to address us
+ * as a slave
+ */
+static int iic_ite_getown(void *data)
+{
+ return (gpi.iic_own);
+}
+
+
+static int iic_ite_getclock(void *data)
+{
+ return (gpi.iic_clock);
+}
+
+
+#if 0
+static void iic_ite_sleep(unsigned long timeout)
+{
+ schedule_timeout( timeout * HZ);
+}
+#endif
+
+
+/* Put this process to sleep. We will wake up when the
+ * IIC controller interrupts.
+ */
+static void iic_ite_waitforpin(void) {
+
+ int timeout = 2;
+
+ /* If interrupts are enabled (which they are), then put the process to
+ * sleep. This process will be awakened by two events -- either the
+ * the IIC peripheral interrupts or the timeout expires.
+ * If interrupts are not enabled then delay for a reasonable amount
+ * of time and return.
+ */
+ if (gpi.iic_irq > 0) {
+ cli();
+ if (iic_pending == 0) {
+ interruptible_sleep_on_timeout(&iic_wait, timeout*HZ );
+ } else
+ iic_pending = 0;
+ sti();
+ } else {
+ udelay(100);
+ }
+}
+
+
+static void iic_ite_handler(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+
+ iic_pending = 1;
+
+ DEB2(printk("iic_ite_handler: in interrupt handler\n"));
+ wake_up_interruptible(&iic_wait);
+}
+
+
+/* Lock the region of memory where I/O registers exist. Request our
+ * interrupt line and register its associated handler.
+ */
+static int iic_hw_resrc_init(void)
+{
+ if (check_region(gpi.iic_base, ITE_IIC_IO_SIZE) < 0 ) {
+ return -ENODEV;
+ } else {
+ request_region(gpi.iic_base, ITE_IIC_IO_SIZE,
+ "i2c (i2c bus adapter)");
+ }
+ if (gpi.iic_irq > 0) {
+ if (request_irq(gpi.iic_irq, iic_ite_handler, 0, "ITE IIC", 0) < 0) {
+ gpi.iic_irq = 0;
+ } else
+ DEB3(printk("Enabled IIC IRQ %d\n", gpi.iic_irq));
+ enable_irq(gpi.iic_irq);
+ }
+ return 0;
+}
+
+
+static void iic_ite_release(void)
+{
+ if (gpi.iic_irq > 0) {
+ disable_irq(gpi.iic_irq);
+ free_irq(gpi.iic_irq, 0);
+ }
+ release_region(gpi.iic_base , 2);
+}
+
+
+static int iic_ite_reg(struct i2c_client *client)
+{
+ return 0;
+}
+
+
+static int iic_ite_unreg(struct i2c_client *client)
+{
+ return 0;
+}
+
+
+static void iic_ite_inc_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+
+static void iic_ite_dec_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+/* ------------------------------------------------------------------------
+ * Encapsulate the above functions in the correct operations structure.
+ * This is only done when more than one hardware adapter is supported.
+ */
+static struct i2c_algo_iic_data iic_ite_data = {
+ NULL,
+ iic_ite_setiic,
+ iic_ite_getiic,
+ iic_ite_getown,
+ iic_ite_getclock,
+ iic_ite_waitforpin,
+ 80, 80, 100, /* waits, timeout */
+};
+
+static struct i2c_adapter iic_ite_ops = {
+ "ITE IIC adapter",
+ I2C_HW_I_IIC,
+ NULL,
+ &iic_ite_data,
+ iic_ite_inc_use,
+ iic_ite_dec_use,
+ iic_ite_reg,
+ iic_ite_unreg,
+};
+
+/* Called when the module is loaded. This function starts the
+ * cascade of calls up through the heirarchy of i2c modules (i.e. up to the
+ * algorithm layer and into to the core layer)
+ */
+static int __init iic_ite_init(void)
+{
+
+ struct iic_ite *piic = &gpi;
+
+ printk(KERN_INFO "Initialize ITE IIC adapter module\n");
+ if (base == 0)
+ piic->iic_base = DEFAULT_BASE;
+ else
+ piic->iic_base = base;
+
+ if (irq == 0)
+ piic->iic_irq = DEFAULT_IRQ;
+ else
+ piic->iic_irq = irq;
+
+ if (clock == 0)
+ piic->iic_clock = DEFAULT_CLOCK;
+ else
+ piic->iic_clock = clock;
+
+ if (own == 0)
+ piic->iic_own = DEFAULT_OWN;
+ else
+ piic->iic_own = own;
+
+ iic_ite_data.data = (void *)piic;
+#if (LINUX_VERSION_CODE >= 0x020301)
+ init_waitqueue_head(&iic_wait);
+#endif
+ if (iic_hw_resrc_init() == 0) {
+ if (i2c_iic_add_bus(&iic_ite_ops) < 0)
+ return -ENODEV;
+ } else {
+ return -ENODEV;
+ }
+ printk(KERN_INFO " found device at %#x irq %d.\n",
+ piic->iic_base, piic->iic_irq);
+ return 0;
+}
+
+
+static void iic_ite_exit(void)
+{
+ i2c_iic_del_bus(&iic_ite_ops);
+ iic_ite_release();
+}
+
+EXPORT_NO_SYMBOLS;
+
+/* If modules is NOT defined when this file is compiled, then the MODULE_*
+ * macros will resolve to nothing
+ */
+MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter");
+
+MODULE_PARM(base, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(clock, "i");
+MODULE_PARM(own, "i");
+MODULE_PARM(i2c_debug,"i");
+
+
+/* Called when module is loaded or when kernel is intialized.
+ * If MODULES is defined when this file is compiled, then this function will
+ * resolve to init_module (the function called when insmod is invoked for a
+ * module). Otherwise, this function is called early in the boot, when the
+ * kernel is intialized. Check out /include/init.h to see how this works.
+ */
+module_init(iic_ite_init);
+
+/* Resolves to module_cleanup when MODULES is defined. */
+module_exit(iic_ite_exit);
diff --git a/drivers/i2c/i2c-algo-ite.c b/drivers/i2c/i2c-algo-ite.c
new file mode 100644
index 000000000..194d1f78a
--- /dev/null
+++ b/drivers/i2c/i2c-algo-ite.c
@@ -0,0 +1,872 @@
+/*
+ -------------------------------------------------------------------------
+ i2c-algo-ite.c i2c driver algorithms for ITE adapters
+
+ Hai-Pao Fan, MontaVista Software, Inc.
+ hpfan@mvista.com or source@mvista.com
+
+ Copyright 2000 MontaVista Software Inc.
+
+ ---------------------------------------------------------------------------
+ This file was highly leveraged from i2c-algo-pcf.c, which was created
+ by Simon G. Vogl and Hans Berglund:
+
+
+ Copyright (C) 1995-1997 Simon G. Vogl
+ 1998-2000 Hans Berglund
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* ------------------------------------------------------------------------- */
+
+/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
+ Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
+ <mbailey@littlefeet-inc.com> */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-ite.h>
+#include "i2c-ite.h"
+
+#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR
+#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04
+#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR
+
+/* ----- global defines ----------------------------------------------- */
+#define DEB(x) if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/
+#define DEBPROTO(x) if (i2c_debug>=9) x;
+ /* debug the protocol by showing transferred bits */
+#define DEF_TIMEOUT 16
+
+/* debugging - slow down transfer to have a look at the data .. */
+/* I use this with two leds&resistors, each one connected to sda,scl */
+/* respectively. This makes sure that the algorithm works. Some chips */
+/* might not like this, as they have an internal timeout of some mils */
+/*
+#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\
+ if (need_resched) schedule();
+*/
+
+
+/* ----- global variables --------------------------------------------- */
+
+#ifdef SLO_IO
+ int jif;
+#endif
+
+/* module parameters:
+ */
+static int i2c_debug=1;
+static int iic_test=0; /* see if the line-setting functions work */
+static int iic_scan=0; /* have a look at what's hanging 'round */
+
+/* --- setting states on the bus with the right timing: --------------- */
+
+#define get_clock(adap) adap->getclock(adap->data)
+#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val)
+#define iic_inw(adap, reg) adap->getiic(adap->data, reg)
+
+
+/* --- other auxiliary functions -------------------------------------- */
+
+static void iic_start(struct i2c_algo_iic_data *adap)
+{
+ iic_outw(adap,ITE_I2CHCR,ITE_CMD);
+}
+
+static void iic_stop(struct i2c_algo_iic_data *adap)
+{
+ iic_outw(adap,ITE_I2CHCR,0);
+ iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI);
+}
+
+static void iic_reset(struct i2c_algo_iic_data *adap)
+{
+ iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80);
+}
+
+
+static int wait_for_bb(struct i2c_algo_iic_data *adap)
+{
+ int timeout = DEF_TIMEOUT;
+ short status;
+
+ status = iic_inw(adap, ITE_I2CHSR);
+#ifndef STUB_I2C
+ while (timeout-- && (status & ITE_I2CHSR_HB)) {
+ udelay(1000); /* How much is this? */
+ status = iic_inw(adap, ITE_I2CHSR);
+ }
+#endif
+ if (timeout<=0) {
+ printk(KERN_ERR "Timeout, host is busy\n");
+ iic_reset(adap);
+ }
+ return(timeout<=0);
+}
+
+/*
+ * Puts this process to sleep for a period equal to timeout
+ */
+static inline void iic_sleep(unsigned long timeout)
+{
+ schedule_timeout( timeout * HZ);
+}
+
+/* After we issue a transaction on the IIC bus, this function
+ * is called. It puts this process to sleep until we get an interrupt from
+ * from the controller telling us that the transaction we requested in complete.
+ */
+static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) {
+
+ int timeout = DEF_TIMEOUT;
+
+ timeout = wait_for_bb(adap);
+ if (timeout) {
+ DEB2(printk("Timeout waiting for host not busy\n");)
+ return -EIO;
+ }
+ timeout = DEF_TIMEOUT;
+
+ *status = iic_inw(adap, ITE_I2CHSR);
+#ifndef STUB_I2C
+ while (timeout-- && !(*status & ITE_I2CHSR_TDI)) {
+ adap->waitforpin();
+ *status = iic_inw(adap, ITE_I2CHSR);
+ }
+#endif
+ if (timeout <= 0)
+ return(-1);
+ else
+ return(0);
+}
+
+static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status)
+{
+ int timeout = DEF_TIMEOUT;
+
+ *status = iic_inw(adap, ITE_I2CFSR);
+#ifndef STUB_I2C
+ while (timeout-- && (*status & ITE_I2CFSR_FE)) {
+ udelay(1000);
+ iic_inw(adap, ITE_I2CFSR);
+ }
+#endif
+ if (timeout <= 0)
+ return(-1);
+ else
+ return(0);
+}
+
+static int iic_init (struct i2c_algo_iic_data *adap)
+{
+ short i;
+
+ /* Clear bit 7 to set I2C to normal operation mode */
+ i=iic_inw(adap, PM_DSR)& 0xff7f;
+ iic_outw(adap, PM_DSR, i);
+
+ /* set IT_GPCCR port C bit 2&3 as function 2 */
+ i = iic_inw(adap, GPIO_CCR) & 0xfc0f;
+ iic_outw(adap,GPIO_CCR,i);
+
+ /* Clear slave address/sub-address */
+ iic_outw(adap,ITE_I2CSAR, 0);
+ iic_outw(adap,ITE_I2CSSAR, 0);
+
+ /* Set clock counter register */
+ iic_outw(adap,ITE_I2CCKCNT, get_clock(adap));
+
+ /* Set START/reSTART/STOP time registers */
+ iic_outw(adap,ITE_I2CSHDR, 0x0a);
+ iic_outw(adap,ITE_I2CRSUR, 0x0a);
+ iic_outw(adap,ITE_I2CPSUR, 0x0a);
+
+ /* Enable interrupts on completing the current transaction */
+ iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE);
+
+ /* Clear transfer count */
+ iic_outw(adap,ITE_I2CFBCR, 0x0);
+
+ DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n",
+ iic_inw(adap, ITE_I2CHSR)));
+ return 0;
+}
+
+
+/*
+ * Sanity check for the adapter hardware - check the reaction of
+ * the bus lines only if it seems to be idle.
+ */
+static int test_bus(struct i2c_algo_iic_data *adap, char *name) {
+#if 0
+ int scl,sda;
+ sda=getsda(adap);
+ if (adap->getscl==NULL) {
+ printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n");
+ return 0;
+ }
+ scl=getscl(adap);
+ printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n",
+ name,getscl(adap),getsda(adap));
+ if (!scl || !sda ) {
+ printk("test_bus: %s seems to be busy.\n",adap->name);
+ goto bailout;
+ }
+ sdalo(adap);
+ printk("test_bus:1 scl: %d sda: %d \n",getscl(adap),
+ getsda(adap));
+ if ( 0 != getsda(adap) ) {
+ printk("test_bus: %s SDA stuck high!\n",name);
+ sdahi(adap);
+ goto bailout;
+ }
+ if ( 0 == getscl(adap) ) {
+ printk("test_bus: %s SCL unexpected low while pulling SDA low!\n",
+ name);
+ goto bailout;
+ }
+ sdahi(adap);
+ printk("test_bus:2 scl: %d sda: %d \n",getscl(adap),
+ getsda(adap));
+ if ( 0 == getsda(adap) ) {
+ printk("test_bus: %s SDA stuck low!\n",name);
+ sdahi(adap);
+ goto bailout;
+ }
+ if ( 0 == getscl(adap) ) {
+ printk("test_bus: %s SCL unexpected low while SDA high!\n",
+ adap->name);
+ goto bailout;
+ }
+ scllo(adap);
+ printk("test_bus:3 scl: %d sda: %d \n",getscl(adap),
+ getsda(adap));
+ if ( 0 != getscl(adap) ) {
+
+ sclhi(adap);
+ goto bailout;
+ }
+ if ( 0 == getsda(adap) ) {
+ printk("test_bus: %s SDA unexpected low while pulling SCL low!\n",
+ name);
+ goto bailout;
+ }
+ sclhi(adap);
+ printk("test_bus:4 scl: %d sda: %d \n",getscl(adap),
+ getsda(adap));
+ if ( 0 == getscl(adap) ) {
+ printk("test_bus: %s SCL stuck low!\n",name);
+ sclhi(adap);
+ goto bailout;
+ }
+ if ( 0 == getsda(adap) ) {
+ printk("test_bus: %s SDA unexpected low while SCL high!\n",
+ name);
+ goto bailout;
+ }
+ printk("test_bus: %s passed test.\n",name);
+ return 0;
+bailout:
+ sdahi(adap);
+ sclhi(adap);
+ return -ENODEV;
+#endif
+ return (0);
+}
+
+/* ----- Utility functions
+ */
+
+
+/* Verify the device we want to talk to on the IIC bus really exists. */
+static inline int try_address(struct i2c_algo_iic_data *adap,
+ unsigned int addr, int retries)
+{
+ int i, ret = -1;
+ short status;
+
+ for (i=0;i<retries;i++) {
+ iic_outw(adap, ITE_I2CSAR, addr);
+ iic_start(adap);
+ if (wait_for_pin(adap, &status) == 0) {
+ if ((status & ITE_I2CHSR_DNE) == 0) {
+ iic_stop(adap);
+ iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
+ ret=1;
+ break; /* success! */
+ }
+ }
+ iic_stop(adap);
+ udelay(adap->udelay);
+ }
+ DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i,
+ addr));
+ return ret;
+}
+
+
+static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf,
+ int count)
+{
+ struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+ int wrcount=0, timeout;
+ short status;
+ int loops, remainder, i, j;
+ union {
+ char byte[2];
+ unsigned short word;
+ } tmp;
+
+ iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]);
+ count--;
+ if (count == 0)
+ return -EIO;
+
+ loops = count / 32; /* 32-byte FIFO */
+ remainder = count % 32;
+
+ if(loops) {
+ for(i=0; i<loops; i++) {
+
+ iic_outw(adap, ITE_I2CFBCR, 32);
+ for(j=0; j<32/2; j++) {
+ tmp.byte[1] = buf[wrcount++];
+ tmp.byte[0] = buf[wrcount++];
+ iic_outw(adap, ITE_I2CFDR, tmp.word);
+ }
+
+ /* status FIFO overrun */
+ iic_inw(adap, ITE_I2CFSR);
+ iic_inw(adap, ITE_I2CFBCR);
+
+ iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
+
+ /* Wait for transmission to complete */
+ timeout = wait_for_pin(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
+ return -EREMOTEIO; /* got a better one ?? */
+ }
+ if (status & ITE_I2CHSR_DB) {
+ iic_stop(adap);
+ printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
+ return -EREMOTEIO; /* got a better one ?? */
+ }
+ }
+ }
+ if(remainder) {
+ iic_outw(adap, ITE_I2CFBCR, remainder);
+ for(i=0; i<remainder/2; i++) {
+ tmp.byte[1] = buf[wrcount++];
+ tmp.byte[0] = buf[wrcount++];
+ iic_outw(adap, ITE_I2CFDR, tmp.word);
+ }
+
+ /* status FIFO overrun */
+ iic_inw(adap, ITE_I2CFSR);
+ iic_inw(adap, ITE_I2CFBCR);
+
+ iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
+
+ timeout = wait_for_pin(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
+ return -EREMOTEIO; /* got a better one ?? */
+ }
+#ifndef STUB_I2C
+ if (status & ITE_I2CHSR_DB) {
+ iic_stop(adap);
+ printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
+ return -EREMOTEIO; /* got a better one ?? */
+ }
+#endif
+ }
+ iic_stop(adap);
+ return wrcount;
+}
+
+
+static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count,
+ int sread)
+{
+ int rdcount=0, i, timeout;
+ short status;
+ struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+ int loops, remainder, j;
+ union {
+ char byte[2];
+ unsigned short word;
+ } tmp;
+
+ loops = count / 32; /* 32-byte FIFO */
+ remainder = count % 32;
+
+ if(loops) {
+ for(i=0; i<loops; i++) {
+ iic_outw(adap, ITE_I2CFBCR, 32);
+ if (sread)
+ iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
+ else
+ iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
+
+ timeout = wait_for_pin(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
+ return (-1);
+ }
+#ifndef STUB_I2C
+ if (status & ITE_I2CHSR_DB) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
+ return (-1);
+ }
+#endif
+
+ timeout = wait_for_fe(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
+ return (-1);
+ }
+
+ for(j=0; j<32/2; j++) {
+ tmp.word = iic_inw(adap, ITE_I2CFDR);
+ buf[rdcount++] = tmp.byte[1];
+ buf[rdcount++] = tmp.byte[0];
+ }
+
+ /* status FIFO underrun */
+ iic_inw(adap, ITE_I2CFSR);
+
+ }
+ }
+
+
+ if(remainder) {
+ remainder=(remainder+1)/2 * 2;
+ iic_outw(adap, ITE_I2CFBCR, remainder);
+ if (sread)
+ iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
+ else
+ iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
+
+ timeout = wait_for_pin(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
+ return (-1);
+ }
+#ifndef STUB_I2C
+ if (status & ITE_I2CHSR_DB) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
+ return (-1);
+ }
+#endif
+ timeout = wait_for_fe(adap, &status);
+ if(timeout) {
+ iic_stop(adap);
+ printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
+ return (-1);
+ }
+
+ for(i=0; i<(remainder+1)/2; i++) {
+ tmp.word = iic_inw(adap, ITE_I2CFDR);
+ buf[rdcount++] = tmp.byte[1];
+ buf[rdcount++] = tmp.byte[0];
+ }
+
+ /* status FIFO underrun */
+ iic_inw(adap, ITE_I2CFSR);
+
+ }
+
+ iic_stop(adap);
+ return rdcount;
+}
+
+
+/* This function implements combined transactions. Combined
+ * transactions consist of combinations of reading and writing blocks of data.
+ * Each transfer (i.e. a read or a write) is separated by a repeated start
+ * condition.
+ */
+#if 0
+static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+ int i;
+ struct i2c_msg *pmsg;
+ int ret;
+
+ DEB2(printk("Beginning combined transaction\n"));
+
+ for(i=0; i<(num-1); i++) {
+ pmsg = &msgs[i];
+ if(pmsg->flags & I2C_M_RD) {
+ DEB2(printk(" This one is a read\n"));
+ ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
+ }
+ else if(!(pmsg->flags & I2C_M_RD)) {
+ DEB2(printk("This one is a write\n"));
+ ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
+ }
+ }
+ /* Last read or write segment needs to be terminated with a stop */
+ pmsg = &msgs[i];
+
+ if(pmsg->flags & I2C_M_RD) {
+ DEB2(printk("Doing the last read\n"));
+ ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
+ }
+ else if(!(pmsg->flags & I2C_M_RD)) {
+ DEB2(printk("Doing the last write\n"));
+ ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
+ }
+
+ return ret;
+}
+#endif
+
+
+/* Whenever we initiate a transaction, the first byte clocked
+ * onto the bus after the start condition is the address (7 bit) of the
+ * device we want to talk to. This function manipulates the address specified
+ * so that it makes sense to the hardware when written to the IIC peripheral.
+ *
+ * Note: 10 bit addresses are not supported in this driver, although they are
+ * supported by the hardware. This functionality needs to be implemented.
+ */
+static inline int iic_doAddress(struct i2c_algo_iic_data *adap,
+ struct i2c_msg *msg, int retries)
+{
+ unsigned short flags = msg->flags;
+ unsigned int addr;
+ int ret;
+
+/* Ten bit addresses not supported right now */
+ if ( (flags & I2C_M_TEN) ) {
+#if 0
+ addr = 0xf0 | (( msg->addr >> 7) & 0x03);
+ DEB2(printk("addr0: %d\n",addr));
+ ret = try_address(adap, addr, retries);
+ if (ret!=1) {
+ printk("iic_doAddress: died at extended address code.\n");
+ return -EREMOTEIO;
+ }
+ iic_outw(adap,msg->addr & 0x7f);
+ if (ret != 1) {
+ printk("iic_doAddress: died at 2nd address code.\n");
+ return -EREMOTEIO;
+ }
+ if ( flags & I2C_M_RD ) {
+ i2c_repstart(adap);
+ addr |= 0x01;
+ ret = try_address(adap, addr, retries);
+ if (ret!=1) {
+ printk("iic_doAddress: died at extended address code.\n");
+ return -EREMOTEIO;
+ }
+ }
+#endif
+ } else {
+
+ addr = ( msg->addr << 1 );
+
+#if 0
+ if (flags & I2C_M_RD )
+ addr |= 1;
+ if (flags & I2C_M_REV_DIR_ADDR )
+ addr ^= 1;
+#endif
+
+ if (iic_inw(adap, ITE_I2CSAR) != addr) {
+ iic_outw(adap, ITE_I2CSAR, addr);
+ ret = try_address(adap, addr, retries);
+ if (ret!=1) {
+ printk("iic_doAddress: died at address code.\n");
+ return -EREMOTEIO;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+
+/* Description: Prepares the controller for a transaction (clearing status
+ * registers, data buffers, etc), and then calls either iic_readbytes or
+ * iic_sendbytes to do the actual transaction.
+ *
+ * still to be done: Before we issue a transaction, we should
+ * verify that the bus is not busy or in some unknown state.
+ */
+static int iic_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+ struct i2c_msg *pmsg;
+ int i = 0;
+ int ret, timeout;
+
+ pmsg = &msgs[i];
+
+ if(!pmsg->len) {
+ DEB2(printk("iic_xfer: read/write length is 0\n");)
+ return -EIO;
+ }
+ if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) {
+ DEB2(printk("iic_xfer: write buffer length is not odd\n");)
+ return -EIO;
+ }
+
+ /* Wait for any pending transfers to complete */
+ timeout = wait_for_bb(adap);
+ if (timeout) {
+ DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");)
+ return -EIO;
+ }
+
+ /* Flush FIFO */
+ iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
+
+ /* Load address */
+ ret = iic_doAddress(adap, pmsg, i2c_adap->retries);
+ if (ret)
+ return -EIO;
+
+#if 0
+ /* Combined transaction (read and write) */
+ if(num > 1) {
+ DEB2(printk("iic_xfer: Call combined transaction\n"));
+ ret = iic_combined_transaction(i2c_adap, msgs, num);
+ }
+#endif
+
+ DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
+ i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
+
+ if(pmsg->flags & I2C_M_RD) /* Read */
+ ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0);
+ else { /* Write */
+ udelay(1000);
+ ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len);
+ }
+
+ if (ret != pmsg->len)
+ DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret));
+ else
+ DEB3(printk("iic_xfer: read/write %d bytes.\n",ret));
+
+ return ret;
+}
+
+
+/* Implements device specific ioctls. Higher level ioctls can
+ * be found in i2c-core.c and are typical of any i2c controller (specifying
+ * slave address, timeouts, etc). These ioctls take advantage of any hardware
+ * features built into the controller for which this algorithm-adapter set
+ * was written. These ioctls allow you to take control of the data and clock
+ * lines and set the either high or low,
+ * similar to a GPIO pin.
+ */
+static int algo_control(struct i2c_adapter *adapter,
+ unsigned int cmd, unsigned long arg)
+{
+
+ struct i2c_algo_iic_data *adap = adapter->algo_data;
+ struct i2c_iic_msg s_msg;
+ char *buf;
+ int ret;
+
+ if (cmd == I2C_SREAD) {
+ if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg,
+ sizeof(struct i2c_iic_msg)))
+ return -EFAULT;
+ buf = kmalloc(s_msg.len, GFP_KERNEL);
+ if (buf== NULL)
+ return -ENOMEM;
+
+ /* Flush FIFO */
+ iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
+
+ /* Load address */
+ iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1);
+ iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff);
+
+ ret = iic_readbytes(adapter, buf, s_msg.len, 1);
+ if (ret>=0) {
+ if(copy_to_user( s_msg.buf, buf, s_msg.len) )
+ ret = -EFAULT;
+ }
+ kfree(buf);
+ }
+ return 0;
+}
+
+
+static u32 iic_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static struct i2c_algorithm iic_algo = {
+ "ITE IIC algorithm",
+ I2C_ALGO_IIC,
+ iic_xfer, /* master_xfer */
+ NULL, /* smbus_xfer */
+ NULL, /* slave_xmit */
+ NULL, /* slave_recv */
+ algo_control, /* ioctl */
+ iic_func, /* functionality */
+};
+
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int i2c_iic_add_bus(struct i2c_adapter *adap)
+{
+ int i;
+ short status;
+ struct i2c_algo_iic_data *iic_adap = adap->algo_data;
+
+ if (iic_test) {
+ int ret = test_bus(iic_adap, adap->name);
+ if (ret<0)
+ return -ENODEV;
+ }
+
+ DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n",
+ adap->name));
+
+ /* register new adapter to i2c module... */
+
+ adap->id |= iic_algo.id;
+ adap->algo = &iic_algo;
+
+ adap->timeout = 100; /* default values, should */
+ adap->retries = 3; /* be replaced by defines */
+ adap->flags = 0;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ i2c_add_adapter(adap);
+ iic_init(iic_adap);
+
+ /* scan bus */
+ /* By default scanning the bus is turned off. */
+ if (iic_scan) {
+ printk(KERN_INFO " i2c-algo-ite: scanning bus %s.\n",
+ adap->name);
+ for (i = 0x00; i < 0xff; i+=2) {
+ iic_outw(iic_adap, ITE_I2CSAR, i);
+ iic_start(iic_adap);
+ if ( (wait_for_pin(iic_adap, &status) == 0) &&
+ ((status & ITE_I2CHSR_DNE) == 0) ) {
+ printk(KERN_INFO "\n(%02x)\n",i>>1);
+ } else {
+ printk(KERN_INFO ".");
+ iic_reset(iic_adap);
+ }
+ udelay(iic_adap->udelay);
+ }
+ }
+ return 0;
+}
+
+
+int i2c_iic_del_bus(struct i2c_adapter *adap)
+{
+ int res;
+ if ((res = i2c_del_adapter(adap)) < 0)
+ return res;
+ DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name));
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+
+int __init i2c_algo_iic_init (void)
+{
+ printk(KERN_INFO "ITE iic (i2c) algorithm module\n");
+ return 0;
+}
+
+
+void i2c_algo_iic_exit(void)
+{
+ return;
+}
+
+
+EXPORT_SYMBOL(i2c_iic_add_bus);
+EXPORT_SYMBOL(i2c_iic_del_bus);
+
+/* The MODULE_* macros resolve to nothing if MODULES is not defined
+ * when this file is compiled.
+ */
+MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
+MODULE_DESCRIPTION("ITE iic algorithm");
+
+MODULE_PARM(iic_test, "i");
+MODULE_PARM(iic_scan, "i");
+MODULE_PARM(i2c_debug,"i");
+
+MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available");
+MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus");
+MODULE_PARM_DESC(i2c_debug,
+ "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol");
+
+
+/* This function resolves to init_module (the function invoked when a module
+ * is loaded via insmod) when this file is compiled with MODULES defined.
+ * Otherwise (i.e. if you want this driver statically linked to the kernel),
+ * a pointer to this function is stored in a table and called
+ * during the intialization of the kernel (in do_basic_setup in /init/main.c)
+ *
+ * All this functionality is complements of the macros defined in linux/init.h
+ */
+module_init(i2c_algo_iic_init);
+
+
+/* If MODULES is defined when this file is compiled, then this function will
+ * resolved to cleanup_module.
+ */
+module_exit(i2c_algo_iic_exit);
diff --git a/drivers/i2c/i2c-ite.h b/drivers/i2c/i2c-ite.h
new file mode 100644
index 000000000..a8ca3c9b5
--- /dev/null
+++ b/drivers/i2c/i2c-ite.h
@@ -0,0 +1,117 @@
+/*
+ --------------------------------------------------------------------
+ i2c-ite.h: Global defines for the I2C controller on board the
+ ITE MIPS processor.
+ --------------------------------------------------------------------
+ Hai-Pao Fan, MontaVista Software, Inc.
+ hpfan@mvista.com or source@mvista.com
+
+ Copyright 2001 MontaVista Software Inc.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef I2C_ITE_H
+#define I2C_ITE_H 1
+
+#include <asm/it8172/it8172.h>
+
+/* I2C Registers */
+#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30
+#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34
+#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38
+#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c
+#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48
+#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c
+#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50
+#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54
+
+#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70
+#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74
+#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78
+#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c
+
+
+/* Host Control Register ITE_I2CHCR */
+#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */
+#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing
+ the current transaction */
+#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */
+#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */
+#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */
+#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute
+ the command and send the data programmed in
+ all required registers to I2C bus */
+#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST
+#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W
+#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R
+#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S
+
+/* Host Status Register ITE_I2CHSR */
+#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except
+ in the first and last bytes */
+#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */
+#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */
+#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */
+#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */
+
+/* Slave Address Register ITE_I2CSAR */
+#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */
+#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the
+ next slave address is addressed */
+
+/* Slave Sub-address Register ITE_I2CSSAR */
+#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */
+
+/* Clock Counter Register ITE_I2CCKCNT */
+#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */
+#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */
+#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */
+
+/* START Hold Time Register ITE_I2CSHDR */
+/* value is counted based on 16 MHz internal clock */
+#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */
+#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */
+
+/* (Repeated) START Setup Time Register ITE_I2CRSUR */
+/* value is counted based on 16 MHz internal clock */
+#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */
+#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */
+
+/* STOP setup Time Register ITE_I2CPSUR */
+
+/* FIFO Data Register ITE_I2CFDR */
+#define ITE_I2CFDR_MASK 0xff
+
+/* FIFO Byte Count Register ITE_I2CFBCR */
+#define ITE_I2CFBCR_MASK 0x3f
+
+/* FIFO Control Register ITE_I2CFCR */
+#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point
+ and I2CFSR */
+/* FIFO Status Register ITE_I2CFSR */
+#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */
+#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */
+#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */
+#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */
+
+#endif /* I2C_ITE_H */
diff --git a/drivers/ide/it8172.c b/drivers/ide/it8172.c
index 937d3e6d1..e40952e08 100644
--- a/drivers/ide/it8172.c
+++ b/drivers/ide/it8172.c
@@ -5,7 +5,7 @@
*
* Copyright 2000 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
- * stevel@mvista.com or support@mvista.com
+ * stevel@mvista.com or source@mvista.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -57,50 +57,41 @@ unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name);
void __init ide_init_it8172 (ide_hwif_t *hwif);
-/*
- * Based on settings done by AMI BIOS
- * (might be usefull if drive is not registered in CMOS for any reason).
- */
static void it8172_tune_drive (ide_drive_t *drive, byte pio)
{
unsigned long flags;
u16 master_data;
- byte slave_data;
+ u32 slave_data;
int is_slave = (&HWIF(drive)->drives[1] == drive);
- int master_port = HWIF(drive)->index ? 0x42 : 0x40;
- int slave_port = 0x44;
- /* ISP RTC */
- byte timings[][2] = { { 0, 0 },
- { 0, 0 },
- { 1, 0 },
- { 2, 1 },
- { 2, 3 }, };
-
+ int master_port = 0x40;
+ int slave_port = 0x44;
+
pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
+ pci_read_config_dword(HWIF(drive)->pci_dev, slave_port, &slave_data);
+
+ /*
+ * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44
+ * are being left at the default values of 8 PCI clocks (242 nsec
+ * for a 33 MHz clock). These can be safely shortened at higher
+ * PIO modes.
+ */
+
if (is_slave) {
- master_data = master_data | 0x4000;
+ master_data |= 0x4000;
if (pio > 1)
- /* enable PPE, IE and TIME */
- master_data = master_data | 0x0070;
- pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data);
- slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0);
- slave_data = slave_data |
- ((timings[pio][0] << 2) | (timings[pio][1]
- << (HWIF(drive)->index ? 4 : 0)));
+ /* enable PPE and IE */
+ master_data |= 0x0060;
} else {
- master_data = master_data & 0xccf8;
+ master_data &= 0xc060;
if (pio > 1)
- /* enable PPE, IE and TIME */
- master_data = master_data | 0x0007;
- master_data = master_data | (timings[pio][0] << 12) |
- (timings[pio][1] << 8);
+ /* enable PPE and IE */
+ master_data |= 0x0006;
}
+
save_flags(flags);
cli();
pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data);
- if (is_slave)
- pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
restore_flags(flags);
}
@@ -150,12 +141,22 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed)
pci_read_config_byte(dev, 0x48, &reg48);
pci_read_config_byte(dev, 0x4a, &reg4a);
+ /*
+ * Setting the DMA cycle time to 2 or 3 PCI clocks (60 and 91 nsec
+ * at 33 MHz PCI clock) seems to cause BadCRC errors during DMA
+ * transfers on some drives, even though both numbers meet the minimum
+ * ATAPI-4 spec of 73 and 54 nsec for UDMA 1 and 2 respectively.
+ * So the faster times are just commented out here. The good news is
+ * that the slower cycle time has very little affect on transfer
+ * performance.
+ */
+
switch(speed) {
case XFER_UDMA_4:
- case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
+ case XFER_UDMA_2: //u_speed = 2 << (drive->dn * 4); break;
case XFER_UDMA_5:
case XFER_UDMA_3:
- case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
+ case XFER_UDMA_1: //u_speed = 1 << (drive->dn * 4); break;
case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
case XFER_MW_DMA_2:
case XFER_MW_DMA_1:
@@ -164,25 +165,16 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed)
}
if (speed >= XFER_UDMA_0) {
- if (!(reg48 & u_flag))
- pci_write_config_byte(dev, 0x48, reg48|u_flag);
- if (!(reg4a & u_speed)) {
- pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed);
- pci_write_config_byte(dev, 0x4a, reg4a|u_speed);
- }
- }
- if (speed < XFER_UDMA_0) {
- if (reg48 & u_flag)
- pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
- if (reg4a & a_speed)
- pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed);
+ pci_write_config_byte(dev, 0x48, reg48 | u_flag);
+ reg4a &= ~a_speed;
+ pci_write_config_byte(dev, 0x4a, reg4a | u_speed);
+ } else {
+ pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
+ pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed);
}
it8172_tune_drive(drive, it8172_dma_2_pio(speed));
-#if IT8172_DEBUG
- printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn);
-#endif
if (!drive->init_speed)
drive->init_speed = speed;
err = ide_config_drive_speed(drive, speed);
@@ -236,8 +228,10 @@ static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
/* Other cases are done by generic IDE-DMA code. */
return ide_dmaproc(func, drive);
}
+
#endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_IT8172_TUNING) */
+
unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name)
{
unsigned char progif;
diff --git a/drivers/sound/ite8172.c b/drivers/sound/ite8172.c
new file mode 100644
index 000000000..bf07c16ea
--- /dev/null
+++ b/drivers/sound/ite8172.c
@@ -0,0 +1,1957 @@
+/*
+ * ite8172.c -- ITE IT8172G Sound Driver.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * stevel@mvista.com or source@mvista.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Module command line parameters:
+ *
+ * Supported devices:
+ * /dev/dsp standard OSS /dev/dsp device
+ * /dev/mixer standard OSS /dev/mixer device
+ *
+ * Notes:
+ *
+ * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are
+ * taken, slightly modified or not at all, from the ES1371 driver,
+ * so refer to the credits in es1371.c for those. The rest of the
+ * code (probe, open, read, write, the ISR, etc.) is new.
+ * 2. The following support is untested:
+ * * Memory mapping the audio buffers, and the ioctl controls that go
+ * with it.
+ * * S/PDIF output.
+ * 3. The following is not supported:
+ * * I2S input.
+ * * legacy audio mode.
+ * 4. Support for volume button interrupts is implemented but doesn't
+ * work yet.
+ *
+ * Revision history
+ * 02.08.2001 0.1 Initial release
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/it8172/it8172.h>
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#define IT8172_DEBUG
+#undef IT8172_VERBOSE_DEBUG
+#define DBG(x) {}
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+
+/*
+ * Audio Controller register bit definitions follow. See
+ * include/asm/it8172/it8172.h for register offsets.
+ */
+
+/* PCM Out Volume Reg */
+#define PCMOV_PCMOM (1<<15) /* PCM Out Mute default 1: mute */
+#define PCMOV_PCMRCG_BIT 8 /* PCM Right channel Gain */
+#define PCMOV_PCMRCG_MASK (0x1f<<PCMOV_PCMRCG_BIT)
+#define PCMOV_PCMLCG_BIT 0 /* PCM Left channel gain */
+#define PCMOV_PCMLCG_MASK 0x1f
+
+/* FM Out Volume Reg */
+#define FMOV_FMOM (1<<15) /* FM Out Mute default 1: mute */
+#define FMOV_FMRCG_BIT 8 /* FM Right channel Gain */
+#define FMOV_FMRCG_MASK (0x1f<<FMOV_FMRCG_BIT)
+#define FMOV_FMLCG_BIT 0 /* FM Left channel gain */
+#define FMOV_FMLCG_MASK 0x1f
+
+/* I2S Out Volume Reg */
+#define I2SV_I2SOM (1<<15) /* I2S Out Mute default 1: mute */
+#define I2SV_I2SRCG_BIT 8 /* I2S Right channel Gain */
+#define I2SV_I2SRCG_MASK (0x1f<<I2SV_I2SRCG_BIT)
+#define I2SV_I2SLCG_BIT 0 /* I2S Left channel gain */
+#define I2SV_I2SLCG_MASK 0x1f
+
+/* Digital Recording Source Select Reg */
+#define DRSS_BIT 0
+#define DRSS_MASK 0x07
+#define DRSS_AC97_PRIM 0
+#define DRSS_FM 1
+#define DRSS_I2S 2
+#define DRSS_PCM 3
+#define DRSS_AC97_SEC 4
+
+/* Playback/Capture Channel Control Registers */
+#define CC_SM (1<<15) /* Stereo, Mone 0: mono 1: stereo */
+#define CC_DF (1<<14) /* Data Format 0: 8 bit 1: 16 bit */
+#define CC_FMT_BIT 14
+#define CC_FMT_MASK (0x03<<CC_FMT_BIT)
+#define CC_CF_BIT 12 /* Channel format (Playback only) */
+#define CC_CF_MASK (0x03<<CC_CF_BIT)
+#define CC_CF_2 0
+#define CC_CF_4 (1<<CC_CF_BIT)
+#define CC_CF_6 (2<<CC_CF_BIT)
+#define CC_SR_BIT 8 /* sample Rate */
+#define CC_SR_MASK (0x0f<<CC_SR_BIT)
+#define CC_SR_5500 0
+#define CC_SR_8000 (1<<CC_SR_BIT)
+#define CC_SR_9600 (2<<CC_SR_BIT)
+#define CC_SR_11025 (3<<CC_SR_BIT)
+#define CC_SR_16000 (4<<CC_SR_BIT)
+#define CC_SR_19200 (5<<CC_SR_BIT)
+#define CC_SR_22050 (6<<CC_SR_BIT)
+#define CC_SR_32000 (7<<CC_SR_BIT)
+#define CC_SR_38400 (8<<CC_SR_BIT)
+#define CC_SR_44100 (9<<CC_SR_BIT)
+#define CC_SR_48000 (10<<CC_SR_BIT)
+#define CC_CSP (1<<7) /* Channel stop
+ * 0: End of Current buffer
+ * 1: Immediately stop when rec stop */
+#define CC_CP (1<<6) /* Channel pause 0: normal, 1: pause */
+#define CC_CA (1<<5) /* Channel Action 0: Stop , 1: start */
+#define CC_CB2L (1<<2) /* Cur. buf. 2 xfr is last 0: No, 1: Yes */
+#define CC_CB1L (1<<1) /* Cur. buf. 1 xfr is last 0: No, 1: Yes */
+#define CC_DE 1 /* DFC/DFIFO Data Empty 1: empty, 0: not empty
+ * (Playback only)
+ */
+
+/* Codec Control Reg */
+#define CODECC_GME (1<<9) /* AC97 GPIO Mode enable */
+#define CODECC_ATM (1<<8) /* AC97 ATE test mode 0: test 1: normal */
+#define CODECC_WR (1<<6) /* AC97 Warn reset 1: warm reset , 0: Normal */
+#define CODECC_CR (1<<5) /* AC97 Cold reset 1: Cold reset , 0: Normal */
+
+
+/* I2S Control Reg */
+#define I2SMC_SR_BIT 6 /* I2S Sampling rate
+ * 00: 48KHz, 01: 44.1 KHz, 10: 32 32 KHz */
+#define I2SMC_SR_MASK (0x03<<I2SMC_SR_BIT)
+#define I2SMC_SR_48000 0
+#define I2SMC_SR_44100 (1<<I2SMC_SR_BIT)
+#define I2SMC_SR_32000 (2<<I2SMC_SR_BIT)
+#define I2SMC_SRSS (1<<5) /* Sample Rate Source Select 1:S/W, 0: H/W */
+#define I2SMC_I2SF_BIT 0 /* I2S Format */
+#define I2SMC_I2SF_MASK 0x03
+#define I2SMC_I2SF_DAC 0
+#define I2SMC_I2SF_ADC 2
+#define I2SMC_I2SF_I2S 3
+
+
+/* Volume up, Down, Mute */
+#define VS_VMP (1<<2) /* Volume mute 1: pushed, 0: not */
+#define VS_VDP (1<<1) /* Volume Down 1: pushed, 0: not */
+#define VS_VUP 1 /* Volime Up 1: pushed, 0: not */
+
+/* SRC, Mixer test control/DFC status reg */
+#define SRCS_DPUSC (1<<5) /* DFC Playback underrun Status/clear */
+#define SRCS_DCOSC (1<<4) /* DFC Capture Overrun Status/clear */
+#define SRCS_SIS (1<<3) /* SRC input select 1: Mixer, 0: Codec I/F */
+#define SRCS_CDIS_BIT 0 /* Codec Data Input Select */
+#define SRCS_CDIS_MASK 0x07
+#define SRCS_CDIS_MIXER 0
+#define SRCS_CDIS_PCM 1
+#define SRCS_CDIS_I2S 2
+#define SRCS_CDIS_FM 3
+#define SRCS_CDIS_DFC 4
+
+
+/* Codec Index Reg command Port */
+#define CIRCP_CID_BIT 10
+#define CIRCP_CID_MASK (0x03<<CIRCP_CID_BIT)
+#define CIRCP_CPS (1<<9) /* Command Port Status 0: ready, 1: busy */
+#define CIRCP_DPVF (1<<8) /* Data Port Valid Flag 0: invalis, 1: valid */
+#define CIRCP_RWC (1<<7) /* Read/write command */
+#define CIRCP_CIA_BIT 0
+#define CIRCP_CIA_MASK 0x007F /* Codec Index Address */
+
+/* Test Mode Control/Test group Select Control */
+
+/* General Control Reg */
+#define GC_VDC_BIT 6 /* Volume Division Control */
+#define GC_VDC_MASK (0x03<<GC_VDC_BIT)
+#define GC_VDC_NONE 0
+#define GC_VDC_DIV2 (1<<GC_VDC_BIT)
+#define GC_VDC_DIV4 (2<<GC_VDC_BIT)
+#define GC_SOE (1<<2) /* S/PDIF Output enable */
+#define GC_SWR 1 /* Software warn reset */
+
+/* Interrupt mask Control Reg */
+#define IMC_VCIM (1<<6) /* Volume CNTL interrupt mask */
+#define IMC_CCIM (1<<1) /* Capture Chan. iterrupt mask */
+#define IMC_PCIM 1 /* Playback Chan. interrupt mask */
+
+/* Interrupt status/clear reg */
+#define ISC_VCI (1<<6) /* Volume CNTL interrupt 1: clears */
+#define ISC_CCI (1<<1) /* Capture Chan. interrupt 1: clears */
+#define ISC_PCI 1 /* Playback Chan. interrupt 1: clears */
+
+/* misc stuff */
+#define POLL_COUNT 0x5000
+
+
+#define IT8172_MODULE_NAME "IT8172 audio"
+#define PFX IT8172_MODULE_NAME ": "
+
+
+/* --------------------------------------------------------------------- */
+
+struct it8172_state {
+ /* list of it8172 devices */
+ struct list_head devs;
+
+ /* the corresponding pci_dev structure */
+ struct pci_dev *dev;
+
+ /* soundcore stuff */
+ int dev_audio;
+
+ /* hardware resources */
+ unsigned long io;
+ unsigned int irq;
+
+ /* PCI ID's */
+ u16 vendor;
+ u16 device;
+ u8 rev; /* the chip revision */
+
+ /* options */
+ int spdif_volume; /* S/PDIF output is enabled if != -1 */
+
+#ifdef IT8172_DEBUG
+ /* debug /proc entry */
+ struct proc_dir_entry *ps;
+ struct proc_dir_entry *ac97_ps;
+#endif /* IT8172_DEBUG */
+
+ struct ac97_codec codec;
+
+ unsigned short pcc, capcc;
+ unsigned dacrate, adcrate;
+
+ spinlock_t lock;
+ struct semaphore open_sem;
+ mode_t open_mode;
+ wait_queue_head_t open_wait;
+
+ struct dmabuf {
+ void *rawbuf;
+ dma_addr_t dmaaddr;
+ unsigned buforder;
+ unsigned numfrag;
+ unsigned fragshift;
+ void* nextIn;
+ void* nextOut;
+ int count;
+ int curBufPtr;
+ unsigned total_bytes;
+ unsigned error; /* over/underrun */
+ wait_queue_head_t wait;
+ /* redundant, but makes calculations easier */
+ unsigned fragsize;
+ unsigned dmasize;
+ unsigned fragsamples;
+ /* OSS stuff */
+ unsigned mapped:1;
+ unsigned ready:1;
+ unsigned stopped:1;
+ unsigned ossfragshift;
+ int ossmaxfrags;
+ unsigned subdivision;
+ } dma_dac, dma_adc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+ unsigned r = 0;
+
+ if (x >= 0x10000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x >= 0x100) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x >= 0x10) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x >= 4) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x >= 2)
+ r++;
+ return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void it8172_delay(int msec)
+{
+ unsigned long tmo;
+ signed long tmo2;
+
+ if (in_interrupt())
+ return;
+
+ tmo = jiffies + (msec*HZ)/1000;
+ for (;;) {
+ tmo2 = tmo - jiffies;
+ if (tmo2 <= 0)
+ break;
+ schedule_timeout(tmo2);
+ }
+}
+
+
+static unsigned short
+get_compat_rate(unsigned* rate)
+{
+ unsigned rate_out = *rate;
+ unsigned short sr;
+
+ if (rate_out >= 46050) {
+ sr = CC_SR_48000; rate_out = 48000;
+ } else if (rate_out >= 41250) {
+ sr = CC_SR_44100; rate_out = 44100;
+ } else if (rate_out >= 35200) {
+ sr = CC_SR_38400; rate_out = 38400;
+ } else if (rate_out >= 27025) {
+ sr = CC_SR_32000; rate_out = 32000;
+ } else if (rate_out >= 20625) {
+ sr = CC_SR_22050; rate_out = 22050;
+ } else if (rate_out >= 17600) {
+ sr = CC_SR_19200; rate_out = 19200;
+ } else if (rate_out >= 13513) {
+ sr = CC_SR_16000; rate_out = 16000;
+ } else if (rate_out >= 10313) {
+ sr = CC_SR_11025; rate_out = 11025;
+ } else if (rate_out >= 8800) {
+ sr = CC_SR_9600; rate_out = 9600;
+ } else if (rate_out >= 6750) {
+ sr = CC_SR_8000; rate_out = 8000;
+ } else {
+ sr = CC_SR_5500; rate_out = 5500;
+ }
+
+ *rate = rate_out;
+ return sr;
+}
+
+static void set_adc_rate(struct it8172_state *s, unsigned rate)
+{
+ unsigned long flags;
+ unsigned short sr;
+
+ sr = get_compat_rate(&rate);
+
+ spin_lock_irqsave(&s->lock, flags);
+ s->capcc &= ~CC_SR_MASK;
+ s->capcc |= sr;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ s->adcrate = rate;
+}
+
+
+static void set_dac_rate(struct it8172_state *s, unsigned rate)
+{
+ unsigned long flags;
+ unsigned short sr;
+
+ sr = get_compat_rate(&rate);
+
+ spin_lock_irqsave(&s->lock, flags);
+ s->pcc &= ~CC_SR_MASK;
+ s->pcc |= sr;
+ outw(s->pcc, s->io+IT_AC_PCC);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ s->dacrate = rate;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static u16 rdcodec(struct ac97_codec *codec, u8 addr)
+{
+ struct it8172_state *s = (struct it8172_state *)codec->private_data;
+ unsigned long flags;
+ unsigned short circp, data;
+ int i;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ for (i = 0; i < POLL_COUNT; i++)
+ if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS))
+ break;
+ if (i == POLL_COUNT)
+ printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n");
+
+ circp = addr & CIRCP_CIA_MASK;
+ circp |= (codec->id << CIRCP_CID_BIT);
+ circp |= CIRCP_RWC; // read command
+ outw(circp, s->io+IT_AC_CIRCP);
+
+ /* now wait for the data */
+ for (i = 0; i < POLL_COUNT; i++)
+ if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF)
+ break;
+ if (i == POLL_COUNT)
+ printk(KERN_INFO PFX "rdcodec: read poll expired!\n");
+
+ data = inw(s->io+IT_AC_CIRDP);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return data;
+}
+
+
+static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+ struct it8172_state *s = (struct it8172_state *)codec->private_data;
+ unsigned long flags;
+ unsigned short circp;
+ int i;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ for (i = 0; i < POLL_COUNT; i++)
+ if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS))
+ break;
+ if (i == POLL_COUNT)
+ printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n");
+
+ circp = addr & CIRCP_CIA_MASK;
+ circp |= (codec->id << CIRCP_CID_BIT);
+ circp &= ~CIRCP_RWC; // write command
+
+ outw(data, s->io+IT_AC_CIRDP); // send data first
+ outw(circp, s->io+IT_AC_CIRCP);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void waitcodec(struct ac97_codec *codec)
+{
+ unsigned short temp;
+
+ /* codec_wait is used to wait for a ready state after
+ an AC97_RESET. */
+ it8172_delay(10);
+
+ temp = rdcodec(codec, 0x26);
+
+ // If power down, power up
+ if (temp & 0x3f00) {
+ // Power on
+ wrcodec(codec, 0x26, 0);
+ it8172_delay(100);
+ // Reread
+ temp = rdcodec(codec, 0x26);
+ }
+
+ // Check if Codec REF,ANL,DAC,ADC ready***/
+ if ((temp & 0x3f0f) != 0x000f) {
+ printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n",
+ temp);
+ return;
+ }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct it8172_state *s)
+{
+ struct dmabuf* db = &s->dma_adc;
+ unsigned long flags;
+ unsigned char imc;
+
+ if (db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L);
+ s->capcc |= CC_CSP;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+
+ // disable capture interrupt
+ imc = inb(s->io+IT_AC_IMC);
+ outb(imc | IMC_CCIM, s->io+IT_AC_IMC);
+
+ db->stopped = 1;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+extern inline void stop_dac(struct it8172_state *s)
+{
+ struct dmabuf* db = &s->dma_dac;
+ unsigned long flags;
+ unsigned char imc;
+
+ if (db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L);
+ s->pcc |= CC_CSP;
+ outw(s->pcc, s->io+IT_AC_PCC);
+
+ // disable playback interrupt
+ imc = inb(s->io+IT_AC_IMC);
+ outb(imc | IMC_PCIM, s->io+IT_AC_IMC);
+
+ db->stopped = 1;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_dac(struct it8172_state *s)
+{
+ struct dmabuf* db = &s->dma_dac;
+ unsigned long flags;
+ unsigned char imc;
+ unsigned long buf1, buf2;
+
+ if (!db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize
+ buf1 = virt_to_bus(db->nextOut);
+ buf2 = buf1 + db->fragsize;
+ if (buf2 >= db->dmaaddr + db->dmasize)
+ buf2 -= db->dmasize;
+
+ outl(buf1, s->io+IT_AC_PCB1STA);
+ outl(buf2, s->io+IT_AC_PCB2STA);
+ db->curBufPtr = IT_AC_PCB1STA;
+
+ // enable playback interrupt
+ imc = inb(s->io+IT_AC_IMC);
+ outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC);
+
+ s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L);
+ s->pcc |= CC_CA;
+ outw(s->pcc, s->io+IT_AC_PCC);
+
+ db->stopped = 0;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_adc(struct it8172_state *s)
+{
+ struct dmabuf* db = &s->dma_adc;
+ unsigned long flags;
+ unsigned char imc;
+ unsigned long buf1, buf2;
+
+ if (!db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize
+ buf1 = virt_to_bus(db->nextIn);
+ buf2 = buf1 + db->fragsize;
+ if (buf2 >= db->dmaaddr + db->dmasize)
+ buf2 -= db->dmasize;
+
+ outl(buf1, s->io+IT_AC_CAPB1STA);
+ outl(buf2, s->io+IT_AC_CAPB2STA);
+ db->curBufPtr = IT_AC_CAPB1STA;
+
+ // enable capture interrupt
+ imc = inb(s->io+IT_AC_IMC);
+ outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC);
+
+ s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L);
+ s->capcc |= CC_CA;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+
+ db->stopped = 0;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+extern inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db)
+{
+ struct page *page, *pend;
+
+ if (db->rawbuf) {
+ /* undo marking the pages as reserved */
+ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+ for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+ mem_map_unreserve(page);
+ pci_free_consistent(s->dev, PAGE_SIZE << db->buforder,
+ db->rawbuf, db->dmaaddr);
+ }
+ db->rawbuf = db->nextIn = db->nextOut = NULL;
+ db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db,
+ unsigned rate, unsigned fmt, unsigned reg)
+{
+ int order;
+ unsigned bytepersec;
+ unsigned bufs;
+ struct page *page, *pend;
+
+ if (!db->rawbuf) {
+ db->ready = db->mapped = 0;
+ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+ if ((db->rawbuf = pci_alloc_consistent(s->dev,
+ PAGE_SIZE << order,
+ &db->dmaaddr)))
+ break;
+ if (!db->rawbuf)
+ return -ENOMEM;
+ db->buforder = order;
+ /* now mark the pages as reserved;
+ otherwise remap_page_range doesn't do what we want */
+ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+ for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+ mem_map_reserve(page);
+ }
+
+ db->count = 0;
+ db->nextIn = db->nextOut = db->rawbuf;
+
+ bytepersec = rate << sample_shift[fmt];
+ bufs = PAGE_SIZE << db->buforder;
+ if (db->ossfragshift) {
+ if ((1000 << db->ossfragshift) < bytepersec)
+ db->fragshift = ld2(bytepersec/1000);
+ else
+ db->fragshift = db->ossfragshift;
+ } else {
+ db->fragshift = ld2(bytepersec/100/(db->subdivision ?
+ db->subdivision : 1));
+ if (db->fragshift < 3)
+ db->fragshift = 3;
+ }
+ db->numfrag = bufs >> db->fragshift;
+ while (db->numfrag < 4 && db->fragshift > 3) {
+ db->fragshift--;
+ db->numfrag = bufs >> db->fragshift;
+ }
+ db->fragsize = 1 << db->fragshift;
+ if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+ db->numfrag = db->ossmaxfrags;
+ db->fragsamples = db->fragsize >> sample_shift[fmt];
+ db->dmasize = db->numfrag << db->fragshift;
+ memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize);
+
+ // set data length register
+ outw(db->fragsize, s->io+reg+2);
+ db->ready = 1;
+
+ return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct it8172_state *s)
+{
+ stop_adc(s);
+ return prog_dmabuf(s, &s->dma_adc, s->adcrate,
+ (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT,
+ IT_AC_CAPCC);
+}
+
+extern inline int prog_dmabuf_dac(struct it8172_state *s)
+{
+ stop_dac(s);
+ return prog_dmabuf(s, &s->dma_dac, s->dacrate,
+ (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT,
+ IT_AC_PCC);
+}
+
+
+/* hold spinlock for the following! */
+
+static void it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct it8172_state *s = (struct it8172_state *)dev_id;
+ struct dmabuf* dac = &s->dma_dac;
+ struct dmabuf* adc = &s->dma_adc;
+ unsigned char isc, vs;
+ unsigned short vol, mute;
+ unsigned long newptr;
+
+ spin_lock(&s->lock);
+
+ isc = inb(s->io+IT_AC_ISC);
+
+ /* fastpath out, to ease interrupt sharing */
+ if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI)))
+ return;
+
+ /* clear audio interrupts first */
+ outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC);
+
+ /* handle volume button events */
+ if (isc & ISC_VCI) {
+ vs = inb(s->io+IT_AC_VS);
+ outb(0, s->io+IT_AC_VS);
+ vol = inw(s->io+IT_AC_PCMOV);
+ mute = vol & PCMOV_PCMOM;
+ vol &= PCMOV_PCMLCG_MASK;
+ if ((vs & VS_VUP) && vol > 0)
+ vol--;
+ if ((vs & VS_VDP) && vol < 0x1f)
+ vol++;
+ vol |= (vol << PCMOV_PCMRCG_BIT);
+ if (vs & VS_VMP)
+ vol |= (mute ^ PCMOV_PCMOM);
+ outw(vol, s->io+IT_AC_PCMOV);
+ }
+
+ /* update capture pointers */
+ if (isc & ISC_CCI) {
+ if (adc->count > adc->dmasize - adc->fragsize) {
+ // Overrun. Stop ADC and log the error
+ stop_adc(s);
+ adc->error++;
+ printk(KERN_INFO PFX "adc overrun\n");
+ } else {
+ newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize;
+ if (newptr >= adc->dmaaddr + adc->dmasize)
+ newptr -= adc->dmasize;
+
+ outl(newptr, s->io+adc->curBufPtr);
+ adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ?
+ IT_AC_CAPB2STA : IT_AC_CAPB1STA;
+
+ adc->nextIn += adc->fragsize;
+ if (adc->nextIn >= adc->rawbuf + adc->dmasize)
+ adc->nextIn -= adc->dmasize;
+
+ adc->count += adc->fragsize;
+ adc->total_bytes += adc->fragsize;
+
+ /* wake up anybody listening */
+ if (waitqueue_active(&adc->wait))
+ wake_up_interruptible(&adc->wait);
+ }
+ }
+
+ /* update playback pointers */
+ if (isc & ISC_PCI) {
+ newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize;
+ if (newptr >= dac->dmaaddr + dac->dmasize)
+ newptr -= dac->dmasize;
+
+ outl(newptr, s->io+dac->curBufPtr);
+ dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ?
+ IT_AC_PCB2STA : IT_AC_PCB1STA;
+
+ dac->nextOut += dac->fragsize;
+ if (dac->nextOut >= dac->rawbuf + dac->dmasize)
+ dac->nextOut -= dac->dmasize;
+
+ dac->count -= dac->fragsize;
+ dac->total_bytes += dac->fragsize;
+
+ /* wake up anybody listening */
+ if (waitqueue_active(&dac->wait))
+ wake_up_interruptible(&dac->wait);
+
+ if (dac->count <= 0)
+ stop_dac(s);
+ }
+
+ spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t it8172_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+
+static int it8172_open_mixdev(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct list_head *list;
+ struct it8172_state *s;
+
+ for (list = devs.next; ; list = list->next) {
+ if (list == &devs)
+ return -ENODEV;
+ s = list_entry(list, struct it8172_state, devs);
+ if (s->codec.dev_mixer == minor)
+ break;
+ }
+ file->private_data = s;
+ return 0;
+}
+
+static int it8172_release_mixdev(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+
+static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
+ unsigned long arg)
+{
+ return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int it8172_ioctl_mixdev(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ struct ac97_codec *codec = &s->codec;
+
+ return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations it8172_mixer_fops = {
+ owner: THIS_MODULE,
+ llseek: it8172_llseek,
+ ioctl: it8172_ioctl_mixdev,
+ open: it8172_open_mixdev,
+ release: it8172_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct it8172_state *s, int nonblock)
+{
+ unsigned long flags;
+ int count, tmo;
+
+ if (s->dma_dac.mapped || !s->dma_dac.ready)
+ return 0;
+
+ for (;;) {
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_dac.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count <= 0)
+ break;
+ if (signal_pending(current))
+ break;
+ if (nonblock)
+ return -EBUSY;
+ tmo = 1000 * count / s->dacrate;
+ tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT];
+ it8172_delay(tmo);
+ }
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t it8172_read(struct file *file, char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ struct dmabuf *db = &s->dma_adc;
+ ssize_t ret;
+ unsigned long flags;
+ int cnt, bufcnt, avail;
+
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (db->mapped)
+ return -ENXIO;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+ ret = 0;
+
+ while (count > 0) {
+ // wait for samples in capture buffer
+ do {
+ spin_lock_irqsave(&s->lock, flags);
+ if (db->stopped)
+ start_adc(s);
+ avail = db->count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ return ret;
+ }
+ interruptible_sleep_on(&db->wait);
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ return ret;
+ }
+ }
+ } while (avail <= 0);
+
+ cnt = count > avail ? avail : count;
+ bufcnt = cnt;
+ if (cnt % db->fragsize) {
+ // round count up to nearest fragment
+ int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize);
+ cnt = newcnt;
+ }
+
+ // copy from nextOut to user
+ if (copy_to_user(buffer, db->nextOut, bufcnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ return ret;
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+ db->count -= cnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ db->nextOut += cnt;
+ if (db->nextOut >= db->rawbuf + db->dmasize)
+ db->nextOut -= db->dmasize;
+
+ count -= bufcnt;
+ buffer += bufcnt;
+ ret += bufcnt;
+ } // while (count > 0)
+
+ return ret;
+}
+
+static ssize_t it8172_write(struct file *file, const char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ struct dmabuf *db = &s->dma_dac;
+ ssize_t ret;
+ unsigned long flags;
+ int cnt, bufcnt, avail;
+
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (db->mapped)
+ return -ENXIO;
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+ ret = 0;
+
+ while (count > 0) {
+ // wait for space in playback buffer
+ do {
+ spin_lock_irqsave(&s->lock, flags);
+ avail = db->dmasize - db->count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ return ret;
+ }
+ interruptible_sleep_on(&db->wait);
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ return ret;
+ }
+ }
+ } while (avail <= 0);
+
+ cnt = count > avail ? avail : count;
+ // copy to nextIn
+ if (copy_from_user(db->nextIn, buffer, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ return ret;
+ }
+
+ bufcnt = cnt;
+ if (cnt % db->fragsize) {
+ // round count up to nearest fragment, and fill remainder of
+ // fragment with silence
+ int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize);
+ memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt);
+ cnt = newcnt;
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+ db->count += cnt;
+ if (db->stopped)
+ start_dac(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ db->nextIn += cnt;
+ if (db->nextIn >= db->rawbuf + db->dmasize)
+ db->nextIn -= db->dmasize;
+
+ count -= bufcnt;
+ buffer += bufcnt;
+ ret += bufcnt;
+ } // while (count > 0)
+
+ return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int it8172_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ unsigned long flags;
+ unsigned int mask = 0;
+
+ if (file->f_mode & FMODE_WRITE)
+ poll_wait(file, &s->dma_dac.wait, wait);
+ if (file->f_mode & FMODE_READ)
+ poll_wait(file, &s->dma_adc.wait, wait);
+ spin_lock_irqsave(&s->lock, flags);
+ if (file->f_mode & FMODE_READ) {
+ if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (s->dma_dac.mapped) {
+ if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ } else {
+ if ((signed)s->dma_dac.dmasize >=
+ s->dma_dac.count + (signed)s->dma_dac.fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ return mask;
+}
+
+static int it8172_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ struct dmabuf *db;
+ unsigned long size;
+
+ lock_kernel();
+ if (vma->vm_flags & VM_WRITE)
+ db = &s->dma_dac;
+ else if (vma->vm_flags & VM_READ)
+ db = &s->dma_adc;
+ else {
+ unlock_kernel();
+ return -EINVAL;
+ }
+ if (vma->vm_pgoff != 0) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+ size = vma->vm_end - vma->vm_start;
+ if (size > (PAGE_SIZE << db->buforder)) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+ if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf),
+ size, vma->vm_page_prot)) {
+ unlock_kernel();
+ return -EAGAIN;
+ }
+ db->mapped = 1;
+ unlock_kernel();
+ return 0;
+}
+
+
+#ifdef IT8172_VERBOSE_DEBUG
+static struct ioctl_str_t {
+ unsigned int cmd;
+ const char* str;
+} ioctl_str[] = {
+ {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
+ {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
+ {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
+ {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
+ {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
+ {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
+ {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
+ {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
+ {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
+ {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
+ {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
+ {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
+ {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
+ {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
+ {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
+ {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
+ {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
+ {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
+ {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
+ {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
+ {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
+ {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
+ {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
+ {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
+ {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
+ {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
+ {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
+ {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
+ {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
+ {OSS_GETVERSION, "OSS_GETVERSION"},
+ {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
+ {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
+ {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
+ {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
+};
+#endif
+
+static int it8172_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+ unsigned long flags;
+ audio_buf_info abinfo;
+ count_info cinfo;
+ int count;
+ int val, mapped, ret, diff;
+
+ mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+ ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+#ifdef IT8172_VERBOSE_DEBUG
+ for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) {
+ if (ioctl_str[count].cmd == cmd)
+ break;
+ }
+ if (count < sizeof(ioctl_str)/sizeof(ioctl_str[0]))
+ printk(KERN_INFO PFX "ioctl %s\n", ioctl_str[count].str);
+ else
+ printk(KERN_INFO PFX "ioctl unknown, 0x%x\n", cmd);
+#endif
+
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, (int *)arg);
+
+ case SNDCTL_DSP_SYNC:
+ if (file->f_mode & FMODE_WRITE)
+ return drain_dac(s, file->f_flags & O_NONBLOCK);
+ return 0;
+
+ case SNDCTL_DSP_SETDUPLEX:
+ return 0;
+
+ case SNDCTL_DSP_GETCAPS:
+ return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
+ DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+
+ case SNDCTL_DSP_RESET:
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ synchronize_irq();
+ s->dma_dac.count = s->dma_dac.total_bytes = 0;
+ s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf;
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ synchronize_irq();
+ s->dma_adc.count = s->dma_adc.total_bytes = 0;
+ s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf;
+ }
+ return 0;
+
+ case SNDCTL_DSP_SPEED:
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (val >= 0) {
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ set_adc_rate(s, val);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ set_dac_rate(s, val);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ }
+ return put_user((file->f_mode & FMODE_READ) ?
+ s->adcrate : s->dacrate, (int *)arg);
+
+ case SNDCTL_DSP_STEREO:
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ if (val)
+ s->capcc |= CC_SM;
+ else
+ s->capcc &= ~CC_SM;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ if (val)
+ s->pcc |= CC_SM;
+ else
+ s->pcc &= ~CC_SM;
+ outw(s->pcc, s->io+IT_AC_PCC);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ return 0;
+
+ case SNDCTL_DSP_CHANNELS:
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (val != 0) {
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ if (val >= 2) {
+ val = 2;
+ s->capcc |= CC_SM;
+ }
+ else
+ s->capcc &= ~CC_SM;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ switch (val) {
+ case 1:
+ s->pcc &= ~CC_SM;
+ break;
+ case 2:
+ s->pcc |= CC_SM;
+ break;
+ default:
+ // FIX! support multichannel???
+ val = 2;
+ s->pcc |= CC_SM;
+ break;
+ }
+ outw(s->pcc, s->io+IT_AC_PCC);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ }
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+ return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+
+ case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (val != AFMT_QUERY) {
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ if (val == AFMT_S16_LE)
+ s->capcc |= CC_DF;
+ else {
+ val = AFMT_U8;
+ s->capcc &= ~CC_DF;
+ }
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ if (val == AFMT_S16_LE)
+ s->pcc |= CC_DF;
+ else {
+ val = AFMT_U8;
+ s->pcc &= ~CC_DF;
+ }
+ outw(s->pcc, s->io+IT_AC_PCC);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ } else {
+ if (file->f_mode & FMODE_READ)
+ val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8;
+ else
+ val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8;
+ }
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_POST:
+ return 0;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+ spin_lock_irqsave(&s->lock, flags);
+ if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
+ val |= PCM_ENABLE_INPUT;
+ if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
+ val |= PCM_ENABLE_OUTPUT;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (file->f_mode & FMODE_READ) {
+ if (val & PCM_ENABLE_INPUT)
+ start_adc(s);
+ else
+ stop_adc(s);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (val & PCM_ENABLE_OUTPUT)
+ start_dac(s);
+ else
+ stop_dac(s);
+ }
+ return 0;
+
+ case SNDCTL_DSP_GETOSPACE:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ abinfo.fragsize = s->dma_dac.fragsize;
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_dac.count;
+ if (!s->dma_dac.stopped)
+ count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL));
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count < 0)
+ count = 0;
+ abinfo.bytes = s->dma_dac.dmasize - count;
+ abinfo.fragstotal = s->dma_dac.numfrag;
+ abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_GETISPACE:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ abinfo.fragsize = s->dma_adc.fragsize;
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_adc.count;
+ if (!s->dma_adc.stopped)
+ count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL));
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count < 0)
+ count = 0;
+ abinfo.bytes = count;
+ abinfo.fragstotal = s->dma_adc.numfrag;
+ abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
+ case SNDCTL_DSP_GETODELAY:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_dac.count;
+ if (!s->dma_dac.stopped)
+ count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL));
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count < 0)
+ count = 0;
+ return put_user(count, (int *)arg);
+
+ case SNDCTL_DSP_GETIPTR:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ cinfo.bytes = s->dma_adc.total_bytes;
+ count = s->dma_adc.count;
+ if (!s->dma_adc.stopped) {
+ diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL);
+ count += diff;
+ cinfo.bytes += diff;
+ cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr;
+ } else
+ cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr;
+ if (s->dma_adc.mapped)
+ s->dma_adc.count &= s->dma_adc.fragsize-1;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count < 0)
+ count = 0;
+ cinfo.blocks = count >> s->dma_adc.fragshift;
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+ case SNDCTL_DSP_GETOPTR:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ cinfo.bytes = s->dma_dac.total_bytes;
+ count = s->dma_dac.count;
+ if (!s->dma_dac.stopped) {
+ diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL);
+ count -= diff;
+ cinfo.bytes += diff;
+ cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr;
+ } else
+ cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr;
+ if (s->dma_dac.mapped)
+ s->dma_dac.count &= s->dma_dac.fragsize-1;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count < 0)
+ count = 0;
+ cinfo.blocks = count >> s->dma_dac.fragshift;
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (file->f_mode & FMODE_WRITE)
+ return put_user(s->dma_dac.fragsize, (int *)arg);
+ else
+ return put_user(s->dma_adc.fragsize, (int *)arg);
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.ossfragshift = val & 0xffff;
+ s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+ if (s->dma_adc.ossfragshift < 4)
+ s->dma_adc.ossfragshift = 4;
+ if (s->dma_adc.ossfragshift > 15)
+ s->dma_adc.ossfragshift = 15;
+ if (s->dma_adc.ossmaxfrags < 4)
+ s->dma_adc.ossmaxfrags = 4;
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.ossfragshift = val & 0xffff;
+ s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+ if (s->dma_dac.ossfragshift < 4)
+ s->dma_dac.ossfragshift = 4;
+ if (s->dma_dac.ossfragshift > 15)
+ s->dma_dac.ossfragshift = 15;
+ if (s->dma_dac.ossmaxfrags < 4)
+ s->dma_dac.ossmaxfrags = 4;
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ return 0;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+ (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+ return -EINVAL;
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if (val != 1 && val != 2 && val != 4)
+ return -EINVAL;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.subdivision = val;
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.subdivision = val;
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+ return 0;
+
+ case SOUND_PCM_READ_RATE:
+ return put_user((file->f_mode & FMODE_READ) ?
+ s->adcrate : s->dacrate, (int *)arg);
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (file->f_mode & FMODE_READ)
+ return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg);
+ else
+ return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ if (file->f_mode & FMODE_READ)
+ return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg);
+ else
+ return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg);
+
+ case SOUND_PCM_WRITE_FILTER:
+ case SNDCTL_DSP_SETSYNCRO:
+ case SOUND_PCM_READ_FILTER:
+ return -EINVAL;
+ }
+
+ return mixdev_ioctl(&s->codec, cmd, arg);
+}
+
+
+static int it8172_open(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ struct list_head *list;
+ struct it8172_state *s;
+ int ret;
+
+ for (list = devs.next; ; list = list->next) {
+ if (list == &devs)
+ return -ENODEV;
+ s = list_entry(list, struct it8172_state, devs);
+ if (!((s->dev_audio ^ minor) & ~0xf))
+ break;
+ }
+ file->private_data = s;
+ /* wait for device to become free */
+ down(&s->open_sem);
+ while (s->open_mode & file->f_mode) {
+ if (file->f_flags & O_NONBLOCK) {
+ up(&s->open_sem);
+ return -EBUSY;
+ }
+ add_wait_queue(&s->open_wait, &wait);
+ __set_current_state(TASK_INTERRUPTIBLE);
+ up(&s->open_sem);
+ schedule();
+ remove_wait_queue(&s->open_wait, &wait);
+ set_current_state(TASK_RUNNING);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ down(&s->open_sem);
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ if (file->f_mode & FMODE_READ) {
+ s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
+ s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
+ s->capcc &= ~(CC_SM | CC_DF);
+ set_adc_rate(s, 8000);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ s->capcc |= CC_DF;
+ outw(s->capcc, s->io+IT_AC_CAPCC);
+ if ((ret = prog_dmabuf_adc(s)))
+ return ret;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
+ s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
+ s->pcc &= ~(CC_SM | CC_DF);
+ set_dac_rate(s, 8000);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ s->pcc |= CC_DF;
+ outw(s->pcc, s->io+IT_AC_PCC);
+ if ((ret = prog_dmabuf_dac(s)))
+ return ret;
+ }
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+ up(&s->open_sem);
+ return 0;
+}
+
+static int it8172_release(struct inode *inode, struct file *file)
+{
+ struct it8172_state *s = (struct it8172_state *)file->private_data;
+
+ lock_kernel();
+ if (file->f_mode & FMODE_WRITE)
+ drain_dac(s, file->f_flags & O_NONBLOCK);
+ down(&s->open_sem);
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ dealloc_dmabuf(s, &s->dma_dac);
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ dealloc_dmabuf(s, &s->dma_adc);
+ }
+ s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+ up(&s->open_sem);
+ wake_up(&s->open_wait);
+ unlock_kernel();
+ return 0;
+}
+
+static /*const*/ struct file_operations it8172_audio_fops = {
+ owner: THIS_MODULE,
+ llseek: it8172_llseek,
+ read: it8172_read,
+ write: it8172_write,
+ poll: it8172_poll,
+ ioctl: it8172_ioctl,
+ mmap: it8172_mmap,
+ open: it8172_open,
+ release: it8172_release,
+};
+
+
+/* --------------------------------------------------------------------- */
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * for debugging purposes, we'll create a proc device that dumps the
+ * CODEC chipstate
+ */
+
+#ifdef IT8172_DEBUG
+static int proc_it8172_dump (char *buf, char **start, off_t fpos,
+ int length, int *eof, void *data)
+{
+ struct it8172_state *s;
+ int cnt, len = 0;
+
+ if (list_empty(&devs))
+ return 0;
+ s = list_entry(devs.next, struct it8172_state, devs);
+
+ /* print out header */
+ len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n");
+
+ // print out digital controller state
+ len += sprintf (buf + len, "IT8172 Audio Controller registers\n");
+ len += sprintf (buf + len, "---------------------------------\n");
+ cnt=0;
+ while (cnt < 0x72) {
+ if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA ||
+ cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA ||
+ cnt == IT_AC_PFDP) {
+ len+= sprintf (buf + len, "reg %02x = %08x\n",
+ cnt, inl(s->io+cnt));
+ cnt += 4;
+ } else {
+ len+= sprintf (buf + len, "reg %02x = %04x\n",
+ cnt, inw(s->io+cnt));
+ cnt += 2;
+ }
+ }
+
+ /* print out CODEC state */
+ len += sprintf (buf + len, "\nAC97 CODEC registers\n");
+ len += sprintf (buf + len, "----------------------\n");
+ for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
+ len+= sprintf (buf + len, "reg %02x = %04x\n",
+ cnt, rdcodec(&s->codec, cnt));
+
+ if (fpos >=len){
+ *start = buf;
+ *eof =1;
+ return 0;
+ }
+ *start = buf + fpos;
+ if ((len -= fpos) > length)
+ return length;
+ *eof =1;
+ return len;
+
+}
+#endif /* IT8172_DEBUG */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static int spdif[NR_DEVICE] = { 0, };
+
+static unsigned int devindex = 0;
+
+MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled");
+
+MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com");
+MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver");
+
+/* --------------------------------------------------------------------- */
+
+static int __devinit it8172_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *pciid)
+{
+ struct it8172_state *s;
+ int i, val;
+ unsigned short pcisr, vol;
+ unsigned char legacy, imc;
+ char proc_str[80];
+
+ if (pcidev->irq == 0)
+ return -1;
+
+ if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) {
+ printk(KERN_ERR PFX "alloc of device struct failed\n");
+ return -1;
+ }
+
+ memset(s, 0, sizeof(struct it8172_state));
+ init_waitqueue_head(&s->dma_adc.wait);
+ init_waitqueue_head(&s->dma_dac.wait);
+ init_waitqueue_head(&s->open_wait);
+ init_MUTEX(&s->open_sem);
+ spin_lock_init(&s->lock);
+ s->dev = pcidev;
+ s->io = pci_resource_start(pcidev, 0);
+ s->irq = pcidev->irq;
+ s->vendor = pcidev->vendor;
+ s->device = pcidev->device;
+ pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
+ s->codec.private_data = s;
+ s->codec.id = 0;
+ s->codec.codec_read = rdcodec;
+ s->codec.codec_write = wrcodec;
+ s->codec.codec_wait = waitcodec;
+
+ if (!request_region(s->io, pci_resource_len(pcidev,0),
+ IT8172_MODULE_NAME)) {
+ printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n",
+ s->io, s->io + pci_resource_len(pcidev,0)-1);
+ goto err_region;
+ }
+ if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT,
+ IT8172_MODULE_NAME, s)) {
+ printk(KERN_ERR PFX "irq %u in use\n", s->irq);
+ goto err_irq;
+ }
+
+ printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq);
+
+ /* register devices */
+ if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0)
+ goto err_dev1;
+ if ((s->codec.dev_mixer =
+ register_sound_mixer(&it8172_mixer_fops, -1)) < 0)
+ goto err_dev2;
+
+#ifdef IT8172_DEBUG
+ /* intialize the debug proc device */
+ s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL,
+ proc_it8172_dump, NULL);
+#endif /* IT8172_DEBUG */
+
+ /*
+ * Reset the Audio device using the IT8172 PCI Reset register. This
+ * creates an audible double click on a speaker connected to Line-out.
+ */
+ IT_IO_READ16(IT_PM_PCISR, pcisr);
+ pcisr |= IT_PM_PCISR_ACSR;
+ IT_IO_WRITE16(IT_PM_PCISR, pcisr);
+ /* wait up to 100msec for reset to complete */
+ for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) {
+ it8172_delay(10);
+ if (i == 10)
+ break;
+ IT_IO_READ16(IT_PM_PCISR, pcisr);
+ }
+ if (i == 10) {
+ printk(KERN_ERR PFX "chip reset timeout!\n");
+ goto err_dev3;
+ }
+
+ /* enable pci io and bus mastering */
+ if (pci_enable_device(pcidev))
+ goto err_dev3;
+ pci_set_master(pcidev);
+
+ /* get out of legacy mode */
+ pci_read_config_byte (pcidev, 0x40, &legacy);
+ pci_write_config_byte (pcidev, 0x40, legacy & ~1);
+
+ s->spdif_volume = -1;
+ /* check to see if s/pdif mode is being requested */
+ if (spdif[devindex]) {
+ printk(KERN_INFO PFX "enabling S/PDIF output\n");
+ s->spdif_volume = 0;
+ outb(GC_SOE, s->io+IT_AC_GC);
+ } else {
+ printk(KERN_INFO PFX "disabling S/PDIF output\n");
+ outb(0, s->io+IT_AC_GC);
+ }
+
+ /* cold reset the AC97 */
+ outw(CODECC_CR, s->io+IT_AC_CODECC);
+ udelay(1000);
+ outw(0, s->io+IT_AC_CODECC);
+ /* need to delay around 500msec(bleech) to give
+ some CODECs enough time to wakeup */
+ it8172_delay(500);
+
+ /* AC97 warm reset to start the bitclk */
+ outw(CODECC_WR, s->io+IT_AC_CODECC);
+ udelay(1000);
+ outw(0, s->io+IT_AC_CODECC);
+
+ /* codec init */
+ if (!ac97_probe_codec(&s->codec))
+ goto err_dev3;
+
+ /* Enable Volume button interrupts */
+ imc = inb(s->io+IT_AC_IMC);
+ outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC);
+
+ /* Un-mute PCM and FM out on the controller */
+ vol = inw(s->io+IT_AC_PCMOV);
+ outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV);
+ vol = inw(s->io+IT_AC_FMOV);
+ outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV);
+
+ /* set channel defaults to 8-bit, mono, 8 Khz */
+ s->pcc = 0;
+ s->capcc = 0;
+ set_dac_rate(s, 8000);
+ set_adc_rate(s, 8000);
+
+ /* set mic to be the recording source */
+ val = SOUND_MASK_MIC;
+ mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+
+ /* mute master and PCM when in S/PDIF mode */
+ if (s->spdif_volume != -1) {
+ val = 0x0000;
+ mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME,
+ (unsigned long)&val);
+ mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM,
+ (unsigned long)&val);
+ }
+
+#ifdef IT8172_DEBUG
+ sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id);
+ s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
+ ac97_read_proc, &s->codec);
+#endif
+
+ /* store it in the driver field */
+ pci_set_drvdata(pcidev, s);
+ pcidev->dma_mask = 0xffffffff;
+ /* put it into driver list */
+ list_add_tail(&s->devs, &devs);
+ /* increment devindex */
+ if (devindex < NR_DEVICE-1)
+ devindex++;
+ return 0;
+
+ err_dev3:
+ unregister_sound_mixer(s->codec.dev_mixer);
+ err_dev2:
+ unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+ printk(KERN_ERR PFX "cannot register misc device\n");
+ free_irq(s->irq, s);
+ err_irq:
+ release_region(s->io, pci_resource_len(pcidev,0));
+ err_region:
+ kfree(s);
+ return -1;
+}
+
+static void __devinit it8172_remove(struct pci_dev *dev)
+{
+ struct it8172_state *s = pci_get_drvdata(dev);
+
+ if (!s)
+ return;
+ list_del(&s->devs);
+#ifdef IT8172_DEBUG
+ if (s->ps)
+ remove_proc_entry(IT8172_MODULE_NAME, NULL);
+#endif /* IT8172_DEBUG */
+ synchronize_irq();
+ free_irq(s->irq, s);
+ release_region(s->io, pci_resource_len(dev,0));
+ unregister_sound_dsp(s->dev_audio);
+ unregister_sound_mixer(s->codec.dev_mixer);
+ kfree(s);
+ pci_set_drvdata(dev, NULL);
+}
+
+
+
+static struct pci_device_id id_table[] __devinitdata = {
+ { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver it8172_driver = {
+ name: IT8172_MODULE_NAME,
+ id_table: id_table,
+ probe: it8172_probe,
+ remove: it8172_remove
+};
+
+static int __init init_it8172(void)
+{
+ if (!pci_present()) /* No PCI bus in this machine! */
+ return -ENODEV;
+ printk("version v0.26 time " __TIME__ " " __DATE__ "\n");
+ return pci_module_init(&it8172_driver);
+}
+
+static void __exit cleanup_it8172(void)
+{
+ printk(KERN_INFO PFX "unloading\n");
+ pci_unregister_driver(&it8172_driver);
+}
+
+module_init(init_it8172);
+module_exit(cleanup_it8172);
+