diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/Kconfig linux-3.8-rc5/drivers/staging/Kconfig
--- linux-3.8-rc5-vanilla/drivers/staging/Kconfig	2013-01-30 11:48:29.814854000 +0300
+++ linux-3.8-rc5/drivers/staging/Kconfig	2013-01-30 11:26:15.341316000 +0300
@@ -26,6 +26,10 @@ if STAGING
 
 source "drivers/staging/et131x/Kconfig"
 
+source "drivers/staging/si3226x/Kconfig"
+
+source "drivers/staging/tdm/Kconfig"
+
 source "drivers/staging/slicoss/Kconfig"
 
 source "drivers/staging/usbip/Kconfig"
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/Makefile linux-3.8-rc5/drivers/staging/Makefile
--- linux-3.8-rc5-vanilla/drivers/staging/Makefile	2013-01-30 11:45:17.118253531 +0300
+++ linux-3.8-rc5/drivers/staging/Makefile	2013-01-30 12:02:10.756456000 +0300
@@ -7,6 +7,8 @@ obj-y				+= media/
 obj-y				+= net/
 obj-$(CONFIG_ET131X)		+= et131x/
 obj-$(CONFIG_SLICOSS)		+= slicoss/
+obj-$(CONFIG_SI3226X)		+= si3226x/
+obj-$(CONFIG_TDM)		+= tdm/
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
 obj-$(CONFIG_W35UND)		+= winbond/
 obj-$(CONFIG_PRISM2_USB)	+= wlan-ng/
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/fifo.c linux-3.8-rc5/drivers/staging/si3226x/fifo.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/fifo.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/fifo.c	2013-01-30 15:31:25.590114000 +0300
@@ -0,0 +1,153 @@
+/**********************************************************************
+ * Author: Michail Kurachkin
+ *
+ * Contact: michail.kurachkin@promwad.com
+ *
+ * Copyright (c) 2013 Promwad Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+**********************************************************************/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "fifo.h"
+
+static void inc_pointer(int *pointer, int item_size, int count_items);
+
+
+/**
+ * Init circular fifo buffer
+ * @param buf - buffer descriptor
+ * @param item_size - item size in bytes
+ * @param count - count items in buffer
+ * @return 0 - ok
+ */
+int init_fifo(struct fifo_buffer *fifo, int item_size, int count)
+{
+	fifo->read_pointer = 0;
+	fifo->write_pointer = 0;
+
+	fifo->buf = kzalloc(item_size * count, GFP_KERNEL);
+	if(!fifo->buf)
+		return -ENOMEM;
+
+	fifo->count = count;
+	fifo->item_size = item_size;
+	memset(fifo->buf, 0, item_size * count);
+	return 0;
+}
+
+
+/**
+ * free fifo buffer
+ * @param fifo
+ */
+void free_fifo(struct fifo_buffer *fifo)
+{
+	if(!fifo)
+		return;
+
+	if(!fifo->buf) {
+		kfree(fifo);
+		return;
+	}
+
+	kfree(fifo->buf);
+}
+
+
+/**
+ * Push into fifo
+ * @param fifo - fifo descriptor
+ * @param item - pointer to item
+ * @return 0 - ok, 1 - item pushed and buffer is full
+ */
+int fifo_push(struct fifo_buffer *fifo, void *item)
+{
+	memcpy(fifo->buf + fifo->write_pointer, item, fifo->item_size);
+
+	inc_pointer(&fifo->write_pointer, fifo->item_size, fifo->count);
+
+	if(fifo->write_pointer == fifo->read_pointer) { // If fifo is full
+		inc_pointer(&fifo->read_pointer, fifo->item_size, fifo->count);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Check fifo buffer is full
+ * @param fifo - fifo descriptor
+ * @return 1 - buffer is full
+ */
+int fifo_is_full(struct fifo_buffer *fifo)
+{
+	int p;
+	p = fifo->write_pointer;
+	inc_pointer(&p, fifo->item_size, fifo->count);
+	if(p == fifo->read_pointer)
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * Check fifo is empty
+ * @param fifo - fifo descriptor
+ * @return 1 - buffer is empty
+ */
+int fifo_is_empty(struct fifo_buffer *fifo)
+{
+	return fifo->read_pointer == fifo->write_pointer;
+}
+
+
+/**
+ * Pop from fifo buffer
+ * @param fifo - fifo descriptor
+ * @param item - pointer to poped item
+ * @return 0 - ok, 1 - buffer is empty
+ */
+int fifo_pop(struct fifo_buffer *fifo, void *item)
+{
+	if(fifo->read_pointer == fifo->write_pointer)
+		return 1;
+
+	memcpy(item, fifo->buf + fifo->read_pointer, fifo->item_size);
+	inc_pointer(&fifo->read_pointer, fifo->item_size, fifo->count);
+	return 0;
+}
+
+
+/**
+ * Increment pointer in circular buffer
+ * @param pointer - pointer in circular buffer
+ * @param item_size - item size in bytes
+ * @param count_items - count items in fifo
+ */
+static void inc_pointer(int *pointer, int item_size, int count_items)
+{
+	(*pointer) += item_size;
+
+	if(*pointer >= (count_items * item_size))
+		*pointer = 0;
+}
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/fifo.h linux-3.8-rc5/drivers/staging/si3226x/fifo.h
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/fifo.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/fifo.h	2012-10-26 13:51:32.000000000 +0300
@@ -0,0 +1,27 @@
+/*
+*  FIFO circular buffers.
+*  Write by Kurochkin Michail 05.08.2010
+*  email: stelhs@ya.ru
+*/
+
+
+#ifndef _FIFO_H_
+#define _FIFO_H_
+
+struct fifo_buffer {
+	u8 *buf;
+	int count;
+	int item_size;
+	int read_pointer;
+	int write_pointer;
+};
+
+int init_fifo(struct fifo_buffer *fifo, int item_width, int count);
+void free_fifo(struct fifo_buffer *fifo);
+
+int fifo_push(struct fifo_buffer *fifo, void *item);
+int fifo_pop(struct fifo_buffer *fifo, void *item);
+int fifo_is_full(struct fifo_buffer *fifo);
+int fifo_is_empty(struct fifo_buffer *fifo);
+
+#endif /* _FIFO_H_ */
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/Kconfig linux-3.8-rc5/drivers/staging/si3226x/Kconfig
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/Kconfig	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/Kconfig	2012-10-31 19:34:56.000000000 +0300
@@ -0,0 +1,9 @@
+config SI3226X
+	tristate "Silicon Labs SLIC driver si3226x"
+	depends on TDM && SPI
+	default n
+	help
+	  This driver supports SLIC chip si3226x.
+	  si3226x provide two FXS ports. For transport
+	  audiodata used tdm framework, for control used
+	  spi framework.
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/Makefile linux-3.8-rc5/drivers/staging/si3226x/Makefile
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/Makefile	2012-11-08 16:23:16.000000000 +0300
@@ -0,0 +1,4 @@
+
+si3226x-y := si3226x_drv.o si3226x_hw.o fifo.o
+
+obj-$(CONFIG_SI3226X) += si3226x.o
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_drv.c linux-3.8-rc5/drivers/staging/si3226x/si3226x_drv.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_drv.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_drv.c	2013-01-30 15:31:03.281726000 +0300
@@ -0,0 +1,1327 @@
+/**********************************************************************
+ * Author: Michail Kurachkin
+ *
+ * Contact: michail.kurachkin@promwad.com
+ *
+ * Copyright (c) 2013 Promwad Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+**********************************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slic_si3226x.h>
+#include <linux/tdm/tdm.h>
+#include <linux/spi/spi.h>
+#include <linux/sched.h>
+#include "si3226x_drv.h"
+#include "si3226x_hw.h"
+#include "fifo.h"
+
+#include <asm/uaccess.h>
+
+/**
+ * spi_dev_list necessary for
+ * accumulate corresponding devices while
+ * this registering. SLIC driver requirement one spi_device,
+ * two tdm_device, and platform_device
+ */
+static LIST_HEAD(slic_dev_list);
+static DEFINE_MUTEX(dev_list_lock);
+
+
+/*
+ * list all existing character devices
+ */
+static LIST_HEAD(slic_chr_dev_list);
+static DEFINE_MUTEX(slic_chr_dev_lock);
+
+/*
+ * class for character devices
+ */
+static struct class *slic_class;
+
+
+/**
+ * slic controls over ioctl
+ * @param slic - slic descriptor
+ * @param cmd - command
+ * @param arg - argument
+ * @return 0 - ok
+ */
+int slic_control(struct si3226x_slic *slic, unsigned int cmd, unsigned long arg)
+{
+	int rc;
+	struct si3226x_line *line = slic->lines;
+	int i;
+
+	switch(cmd) {
+	case SI3226X_SET_COMPANDING_MODE:
+		if(	arg != SI_ALAW && arg !=	SI_MLAW)
+			return -EINVAL;
+
+		for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++) {
+			if (line->state == SI_LINE_DISABLE)
+				continue;
+
+			line->companding_mode = arg;
+
+			rc = slic_setup_audio(line);
+			if (rc)
+				return rc;
+		}
+		break;
+
+	case SI3226X_SET_CALLERID_MODE:
+		if(	arg != SI_FSK_BELLCORE && arg != SI_FSK_ETSI
+		    && arg != SI_CALLERID_DTMF)
+			return -EINVAL;
+
+		for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++) {
+			if (line->state == SI_LINE_DISABLE)
+				continue;
+
+			line->callerid_mode = arg;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * slic line controls over ioctl
+ * @param line - line descriptor
+ * @param cmd - command
+ * @param arg - argument
+ * @return 0 - ok
+ */
+int line_control(struct si3226x_line *line, unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+	u8 data;
+	struct si3226x_caller_id caller_id;
+	u8 callerid_buffer[CALLERID_BUF_SIZE];
+
+	switch(cmd) {
+	case SI3226X_SET_CALLERID_MODE:
+		if( arg != SI_FSK_BELLCORE && arg != SI_FSK_ETSI
+		    && arg != SI_CALLERID_DTMF && arg != SI_CALLERID_NONE)
+			return -EINVAL;
+
+		line->callerid_mode = arg;
+		break;
+
+	case SI3226X_SET_ECHO_CANCELATION:
+		if(arg)
+			rc = slic_enable_echo(line);
+		else
+			rc = slic_disable_echo(line);
+		break;
+
+	case SI3226X_SET_LINE_STATE:
+		rc = slic_set_line_state(line, arg);
+		break;
+
+	case SI3226X_CALL:
+		copy_from_user(&caller_id, (__u8 __user *)arg, sizeof caller_id);
+
+		if(caller_id.size > CALLERID_BUF_SIZE)
+			caller_id.size = CALLERID_BUF_SIZE;
+
+		copy_from_user(callerid_buffer, (__u8 __user *)caller_id.data, caller_id.size);
+		rc = slic_line_call(line, callerid_buffer, caller_id.size);
+		break;
+
+	case SI3226X_SEND_DTMF:
+		rc = slic_send_dtmf_digit(line, arg);
+		break;
+
+	case SI3226X_GET_HOOK_STATE:
+		__put_user(line->hook_state, (__u8 __user *)arg);
+		break;
+
+	case SI3226X_GET_DTMF_DIGIT:
+		rc = fifo_pop(&line->dtmf, &data);
+		if (rc)
+			return -ENODATA;
+
+		__put_user(data, (__u8 __user *)arg);
+		break;
+
+	case SI3226X_GET_AUDIO_BLOCK_SIZE:
+		__put_user(line->audio_buffer_size, (__u8 __user *)arg);
+		break;
+
+	case SI3226X_SET_COMPANDING_MODE:
+		if(	arg != SI_ALAW && arg != SI_MLAW)
+			return -EINVAL;
+
+		line->companding_mode = arg;
+
+		rc = slic_setup_audio(line);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI3226X_ENABLE_AUDIO:
+		if (line->tdm_dev == NULL)
+			return -ENODEV;
+
+		/* TODO: workaround to fix sound distortion after asterisk restart */
+		if (!line->audio_start_flag) {
+			rc = tdm_run_audio(line->tdm_dev);
+			if (!rc)
+				line->audio_start_flag = 1;
+		}
+
+		break;
+
+	case SI3226X_DISABLE_AUDIO:
+		if (line->tdm_dev == NULL)
+			return -ENODEV;
+
+		rc = tdm_stop_audio(line->tdm_dev);
+		break;
+	}
+
+	return rc;
+}
+
+/*
+ * method "open" for character device
+ */
+static int slic_chr_open(struct inode *inode, struct file *file)
+{
+	struct slic_chr_dev *chr_dev;
+	struct si3226x_slic *slic;
+	struct si3226x_line *line;
+	struct tdm_device *tdm_dev;
+	struct tdm_voice_channel *ch;
+	int status = -ENXIO;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	list_for_each_entry(chr_dev, &slic_chr_dev_list, list) {
+		switch (chr_dev->type) {
+		case SLIC_CHR_DEV:
+			slic = dev_get_drvdata(chr_dev->dev);
+
+			if (slic->devt != inode->i_rdev)
+				continue;;
+
+			if (slic->file_opened) {
+				status = -EBUSY;
+				goto out;
+			}
+
+			slic->file_opened = 1;
+			status = 0;
+			break;
+
+		case LINE_CHR_DEV:
+			line = dev_get_drvdata(chr_dev->dev);
+			tdm_dev = line->tdm_dev;
+
+			if (line->devt != inode->i_rdev)
+				continue;
+
+			if (line->file_opened) {
+				status = -EBUSY;
+				goto out;
+			}
+
+			line->audio_buffer_size = tdm_get_voice_block_size(tdm_dev);
+
+			line->rx_buffer = kzalloc(line->audio_buffer_size, GFP_KERNEL);
+			if (!line->rx_buffer) {
+				status = -ENOMEM;
+				goto out;
+			}
+
+			line->tx_buffer = kzalloc(line->audio_buffer_size, GFP_KERNEL);
+			if (!line->tx_buffer) {
+				status = -ENOMEM;
+				goto out;
+			}
+
+			/* store pointer to transmit wait_queue_head_t for use in poll() */
+			ch = tdm_dev->ch;
+			line->tx_wait_queue = &ch->tx_queue;
+			line->rx_wait_queue = &ch->rx_queue;
+			line->file_opened = 1;
+			status = 0;
+
+			break;
+		}
+
+		file->private_data = chr_dev;
+		status = 0;
+		goto out;
+	}
+
+out:
+	mutex_unlock(&slic_chr_dev_lock);
+	return status;
+}
+
+
+/*
+ * method "release" for character device
+ */
+static int slic_chr_release(struct inode *inode, struct file *file)
+{
+	struct slic_chr_dev *chr_dev = file->private_data;
+	struct si3226x_slic *slic;
+	struct si3226x_line *line;
+	struct tdm_device *tdm_dev;
+	int status = -ENXIO;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	switch (chr_dev->type) {
+	case SLIC_CHR_DEV:
+		slic = dev_get_drvdata(chr_dev->dev);
+
+		if(!slic->file_opened) {
+			status = -ENODEV;
+			goto out;
+		}
+
+		slic->file_opened = 0;
+		status = 0;
+		break;
+
+	case LINE_CHR_DEV:
+		line = dev_get_drvdata(chr_dev->dev);
+		tdm_dev = line->tdm_dev;
+
+		if(!line->file_opened) {
+			status = -ENODEV;
+			goto out;
+		}
+
+		kfree(line->rx_buffer);
+		kfree(line->tx_buffer);
+		line->file_opened = 0;
+		status = 0;
+	}
+
+	file->private_data = NULL;
+
+out:
+	mutex_unlock(&slic_chr_dev_lock);
+
+	return status;
+}
+
+/*
+ * method "ioctl" for character device
+ */
+static long
+slic_chr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct slic_chr_dev *chr_dev = file->private_data;
+	struct si3226x_slic *slic;
+	struct si3226x_line *line;
+	int status = -ENXIO;
+
+	if (_IOC_TYPE(cmd) != SI3226X_IOC_MAGIC)
+		return -ENOTTY;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	if (!file->private_data) {
+		status = -EPERM;
+		goto out;
+	}
+
+	switch(chr_dev->type) {
+	case SLIC_CHR_DEV:
+		slic = dev_get_drvdata(chr_dev->dev);
+		if (!slic->file_opened) {
+			status = -ENODEV;
+			goto out;
+		}
+
+		status = slic_control(slic, cmd, arg);
+		break;
+
+	case LINE_CHR_DEV:
+		line = dev_get_drvdata(chr_dev->dev);
+		if (!line->file_opened) {
+			status = -ENODEV;
+			goto out;
+		}
+
+		status = line_control(line, cmd, arg);
+		break;
+	}
+
+out:
+	mutex_unlock(&slic_chr_dev_lock);
+	return status;
+}
+
+
+/*
+ * method "read" for character device
+ */
+static ssize_t
+slic_chr_read(struct file *file, char *buff, size_t count, loff_t *offp)
+{
+	struct slic_chr_dev *chr_dev = file->private_data;
+	struct si3226x_line *line;
+	struct tdm_device *tdm_dev;
+	int rc;
+
+	if (!file->private_data)
+		return -EPERM;
+
+	if(chr_dev->type != LINE_CHR_DEV)
+		return -EPERM;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	line = dev_get_drvdata(chr_dev->dev);
+	tdm_dev = line->tdm_dev;
+
+	if (count != line->audio_buffer_size) {
+		rc = -ENODATA;
+		goto out;
+	}
+
+	rc = tdm_recv(tdm_dev, line->rx_buffer);
+	if (rc) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = copy_to_user(buff, line->rx_buffer, count);
+	if (rc) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = count;
+out:
+	mutex_unlock(&slic_chr_dev_lock);
+	return rc;
+}
+
+
+/*
+ * method "write" for character device
+ */
+static ssize_t
+slic_chr_write(struct file *file, const char *buf, size_t count, loff_t *offp)
+{
+	struct slic_chr_dev *chr_dev = file->private_data;
+	struct si3226x_line *line;
+	struct tdm_device *tdm_dev;
+	int rc;
+
+	if (!file->private_data)
+		return -EPERM;
+
+	if (chr_dev->type != LINE_CHR_DEV)
+		return -EPERM;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	line = dev_get_drvdata(chr_dev->dev);
+	tdm_dev = line->tdm_dev;
+
+	if (count != line->audio_buffer_size) {
+		rc = -EMSGSIZE;
+		goto out;
+	}
+
+	rc = copy_from_user(line->tx_buffer, buf, count);
+	if (rc) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = tdm_send(tdm_dev, line->tx_buffer);
+	if (rc) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = line->audio_buffer_size;
+out:
+	mutex_unlock(&slic_chr_dev_lock);
+	return rc;
+}
+
+
+/*
+ * method "poll" for character device
+ */
+static unsigned int slic_chr_poll(struct file *file, poll_table *wait)
+{
+	struct slic_chr_dev *chr_dev = file->private_data;
+	struct si3226x_line *line;
+	int mask = 0;
+
+	if (!file->private_data)
+		return -EPERM;
+
+	if (chr_dev->type != LINE_CHR_DEV)
+		return -EPERM;
+
+	mutex_lock(&slic_chr_dev_lock);
+
+	line = dev_get_drvdata(chr_dev->dev);
+
+	poll_wait(file, line->rx_wait_queue,  wait);
+	poll_wait(file, line->tx_wait_queue,  wait);
+
+	if (tdm_poll_rx(line->tdm_dev))
+		mask |= POLLIN;
+
+	if (tdm_poll_tx(line->tdm_dev))
+		mask |= POLLOUT;
+
+	mutex_unlock(&slic_chr_dev_lock);
+
+	return mask;
+}
+
+
+/*
+ * file operations for slic character devices
+ */
+static struct file_operations slic_cnt_ops = {
+	.unlocked_ioctl = slic_chr_ioctl,
+	.open = slic_chr_open,
+	.read = slic_chr_read,
+	.write = slic_chr_write,
+	.poll = slic_chr_poll,
+	.release = slic_chr_release,
+	.llseek = no_llseek,
+	.owner = THIS_MODULE,
+};
+
+
+/**
+ * add to list slic character device.
+ * @param dev - character device
+ * @param type - type of character device
+ * @return 0 - ok
+ */
+static int add_slic_chr_dev(struct device *dev, enum chr_dev_type type)
+{
+	struct slic_chr_dev *chr_dev;
+
+	chr_dev = kzalloc(sizeof(*chr_dev), GFP_KERNEL);
+	if (!chr_dev)
+		return -ENOMEM;
+
+	chr_dev->dev = dev;
+	chr_dev->type = type;
+	list_add(&chr_dev->list, &slic_chr_dev_list);
+
+	return 0;
+}
+
+
+/**
+ * delete slic character device form list
+ * @param dev - character device
+ * @return 0 - ok
+ */
+static int del_slic_chr_dev(struct device *dev)
+{
+	struct slic_chr_dev *chr_dev, *chr_dev_tmp;
+
+	list_for_each_entry_safe(chr_dev, chr_dev_tmp, &slic_chr_dev_list, list) {
+		if (chr_dev->dev == dev) {
+			list_del(&chr_dev->list);
+			kfree(chr_dev);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+
+/**
+ * Init slic driver
+ * @return 0 - ok
+ */
+static int init_slic_drv(struct si3226x_slic *slic)
+{
+	struct platform_device *pdev = slic->pdev;
+	struct si3226x_platform_data *plat = 	pdev->dev.platform_data;
+	struct si3226x_line *line = slic->lines;
+	struct device *chr_dev;
+	int rc;
+	int i;
+
+	dev_info(&pdev->dev, "run Initialization slic driver\n");
+
+	/* set default companding_mode for all lines */
+	for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++)
+		line->companding_mode = plat->companding_mode;
+
+	/* request reset GPIO */
+	rc = gpio_request(slic->reset_gpio, DRIVER_NAME "_reset");
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request " DRIVER_NAME "_reset\n");
+		goto out0;
+	}
+	gpio_direction_output(slic->reset_gpio, 1);
+
+	/* request interrupt GPIO */
+	rc = gpio_request(slic->int_gpio, DRIVER_NAME "_irq");
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request " DRIVER_NAME "_irq\n");
+		goto out1;
+	}
+	gpio_direction_input(slic->int_gpio);
+
+	dev_info(&pdev->dev, "GPIO requested\n");
+
+	slic->irq = gpio_to_irq(slic->int_gpio);
+
+	dev_info(&pdev->dev, "slic->int_gpio = %d\n", slic->int_gpio);
+	dev_info(&pdev->dev, "slic->irq = %d\n", slic->irq);
+
+	INIT_WORK(&slic->irq_work, slic_irq_callback);
+
+#ifdef CONFIG_SI3226X_POLLING
+	INIT_DELAYED_WORK(&slic->delayed_work, slic_delayed_work);
+#endif
+
+
+	/* register slic character device */
+	rc = register_chrdev(SI3226X_MAJOR, DRIVER_NAME, &slic_cnt_ops);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "can not register character device\n");
+		goto out2;
+	}
+
+	/* added class device and create /dev/si3226x_cnt.X */
+	slic->devt = MKDEV(SI3226X_MAJOR, pdev->id);
+	chr_dev = device_create(slic_class, &pdev->dev, slic->devt,
+	                        slic, DRIVER_NAME "_cnt.%d", pdev->id);
+
+	if (IS_ERR(chr_dev)) {
+		dev_err(&pdev->dev, "can not added class device\n");
+		goto out3;
+	}
+
+	/*  added character device into device list
+	    for use in file operations
+	*/
+	rc = add_slic_chr_dev(chr_dev, SLIC_CHR_DEV);
+	if (rc) {
+		dev_err(&pdev->dev, "can not added character device\n");
+		goto out4;
+	}
+
+	line = slic->lines;
+	for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++) {
+		if (plat->fxs_tdm_ch[i] < 0) {
+			line->state = SI_LINE_DISABLE;
+			continue;
+		}
+
+		INIT_WORK(&line->line_call_work, do_line_call);
+
+		line->state = SI_LINE_SILENCE;
+
+		/* added class device and create /dev/si3226x_fxsX.X */
+		line->devt = MKDEV(SI3226X_MAJOR, pdev->id + 1 + i);
+		chr_dev = device_create(slic_class, &pdev->dev, line->devt,
+		                        line, DRIVER_NAME "_fxs%d.%d", i, pdev->id);
+
+		if (IS_ERR(chr_dev)) {
+			dev_err(&pdev->dev, "can not added class device\n");
+			goto out4;
+		}
+
+		/*  added created character device into device list
+		    for use in file operations
+		*/
+		rc = add_slic_chr_dev(chr_dev, LINE_CHR_DEV);
+		if (rc) {
+			dev_err(&pdev->dev, "can't added character device\n");
+			goto out4;
+		}
+	}
+
+	rc = init_slic(slic);
+	if (rc) {
+		dev_err(&pdev->dev, "slic initialization fail\n");
+		goto out4;
+	}
+
+#ifdef CONFIG_SI3226X_POLLING
+	dev_info(&pdev->dev, "schedule delayed work\n");
+	schedule_delayed_work(&slic->delayed_work, MSEC(50));
+#else
+	dev_info(&pdev->dev, "request irq\n");
+
+	/* set interrupt callback slic_irq */
+	rc = request_irq(slic->irq, slic_irq,
+	                 IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+	                 dev_name(&slic->pdev->dev), slic);
+
+	if (rc) {
+		dev_err(&pdev->dev, "can not request IRQ\n");
+		rc = -EINVAL;
+		goto out5;
+	}
+#endif
+
+	dev_info(&pdev->dev, "success initialization slic driver\n");
+	return 0;
+
+out5:
+	free_irq(slic->irq, slic);
+	deinit_slic(slic);
+
+out4:
+	{
+		/* remove all character devices */
+		struct slic_chr_dev *chr_dev, *chr_dev_tmp;
+		list_for_each_entry_safe(chr_dev, chr_dev_tmp,
+		                         &slic_chr_dev_list, list) {
+			device_del(chr_dev->dev);
+			del_slic_chr_dev(chr_dev->dev);
+		}
+	}
+
+out3:
+	unregister_chrdev(SI3226X_MAJOR, DRIVER_NAME);
+
+out2:
+	gpio_free(slic->int_gpio);
+
+out1:
+	gpio_free(slic->reset_gpio);
+
+out0:
+	return rc;
+}
+
+
+
+/**
+ * DeInit slic driver
+ */
+void release_slic_drv(struct si3226x_slic *slic)
+{
+	struct platform_device *pdev = slic->pdev;
+	struct slic_chr_dev *chr_dev, *chr_dev_tmp;
+	int i;
+
+	dev_dbg(&pdev->dev, "release slic\n");
+
+	free_irq(slic->irq, slic);
+
+	/* delete slic character device */
+	list_for_each_entry_safe(chr_dev, chr_dev_tmp, &slic_chr_dev_list, list) {
+		struct si3226x_line *line;
+		struct si3226x_slic *s;
+		struct si3226x_line *l;
+
+		switch (chr_dev->type) {
+		case SLIC_CHR_DEV:
+			s = dev_get_drvdata(chr_dev->dev);
+			if (s == slic) {
+				device_del(chr_dev->dev);
+				list_del(&chr_dev->list);
+				kfree(chr_dev);
+			}
+			break;
+
+		case LINE_CHR_DEV:
+			l = dev_get_drvdata(chr_dev->dev);
+			for (i = 0, line = slic->lines;
+					i < SI3226X_MAX_CHANNELS; i++, line++)
+				if (l == line) {
+					device_del(chr_dev->dev);
+					list_del(&chr_dev->list);
+					kfree(chr_dev);
+				}
+			break;
+		}
+	}
+
+	unregister_chrdev(SI3226X_MAJOR, DRIVER_NAME);
+	gpio_free(slic->int_gpio);
+	gpio_free(slic->reset_gpio);
+
+	deinit_slic(slic);
+}
+
+
+
+/**
+ * Add device to slic devices list
+ * @param dev - device (spi, tdm or platform)
+ * @return 0 - ok
+ */
+static int add_to_slic_devices_list(struct device *dev, enum slic_dev_type type)
+{
+	struct slic_dev_list *slic_dev_item;
+
+	slic_dev_item = kzalloc(sizeof *slic_dev_item, GFP_KERNEL);
+	if (!slic_dev_item)
+		return -ENOMEM;
+
+	slic_dev_item->dev = dev;
+	slic_dev_item->type = type;
+	list_add_tail(&slic_dev_item->list, &slic_dev_list);
+
+	return 0;
+}
+
+
+/**
+ * remove device from slic devices list
+ * @param dev - device (spi, tdm or platform)
+ * @return 0 - ok
+ */
+static int remove_from_slic_devices_list(struct device *dev)
+{
+	struct slic_dev_list *slic_dev_item, *slic_dev_item_tmp;
+
+	list_for_each_entry_safe(slic_dev_item, slic_dev_item_tmp, &slic_dev_list, list)
+		if(slic_dev_item->dev == dev) {
+			list_del(&slic_dev_item->list);
+			kzfree(slic_dev_item);
+			return 0;
+		}
+
+	return -ENODEV;
+}
+
+
+/**
+ * allocate memory and configure slic descriptor.
+ * @param pdev - platform device
+ * @return allocated slic controller descriptor
+ */
+struct si3226x_slic *alloc_slic(struct platform_device *pdev) {
+	int i;
+	struct si3226x_slic 	*slic;
+	struct si3226x_line *line;
+
+	slic = kzalloc(sizeof *slic, GFP_KERNEL);
+	if (!slic)
+		return NULL;
+
+	memset(slic, 0, sizeof *slic);
+	platform_set_drvdata(pdev, slic);
+
+	slic->pdev = pdev;
+
+	line = slic->lines;
+	for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++) {
+		line->ch = i;
+		line->audio_start_flag = 0;
+	}
+
+	return slic;
+}
+
+
+/**
+ * free memory allocated alloc_slic()
+ * @param slic - slic controller descriptor
+ * @return 0 - ok
+ */
+int free_slic(struct si3226x_slic *slic)
+{
+	if (slic == NULL)
+		return -EINVAL;
+
+	platform_set_drvdata(slic->pdev, NULL);
+	kfree(slic);
+
+	return 0;
+}
+
+/**
+ * match all devices necessery for slic
+ * @param slic - pointer to pointer on slic private data
+ * @return 0 - ok, 1 - incomplete device list, 0< - any errors
+ */
+int slic_match_bus_devices(struct si3226x_slic **slic)
+{
+	struct slic_dev_list *plat_list_item;
+	struct slic_dev_list *spi_list_item;
+	struct slic_dev_list *tdm_list_item;
+	int i;
+
+
+	list_for_each_entry(plat_list_item, &slic_dev_list, list) {
+		struct platform_device *pdev =
+				to_platform_device(plat_list_item->dev);
+		struct si3226x_platform_data *plat = pdev->dev.platform_data;
+		struct tdm_device *found_tdm_devices[SI3226X_MAX_CHANNELS];
+		struct spi_device *found_spi_dev = NULL;
+		struct si3226x_line *slic_line;
+		int count_found_tdm;
+		int count_fxs = 0;
+
+		if (plat_list_item->type != PLAT_DEVICE)
+			continue;
+
+		/* increment count enabled fxs ports */
+		for (i = 0; i < SI3226X_MAX_CHANNELS; i++)
+			if(plat->fxs_tdm_ch[i] >= 0)
+				count_fxs++;
+
+		if (!count_fxs) {
+			dev_err(&pdev->dev, "Init slic fail. Disabled all FXS ports\n");
+			return -EINVAL;
+		}
+
+		dev_dbg(&pdev->dev, "count_fxs = %d\n", count_fxs);
+
+		/* foreach all spi devices and match with current slic */
+		list_for_each_entry(spi_list_item, &slic_dev_list, list) {
+			struct spi_device *spi_dev = to_spi_device(spi_list_item->dev);
+
+			if (spi_list_item->type != SPI_DEVICE)
+				continue;
+
+			if(plat->spi_chip_select != spi_dev->chip_select)
+				continue;
+
+			found_spi_dev = to_spi_device(spi_list_item->dev);
+		}
+
+		if (found_spi_dev == NULL) {
+			dev_dbg(&pdev->dev, "not found_spi_dev\n");
+			return 1;
+		}
+
+		dev_dbg(&pdev->dev, "found_spi_dev = %s\n",
+		        dev_name(&found_spi_dev->dev));
+
+		/* foreach all registered tdm devices for current slic */
+		/* and match tdm channel with fxs port tdm channel */
+		memset(found_tdm_devices, 0, sizeof *found_tdm_devices);
+		count_found_tdm = 0;
+		for (i = 0; i < SI3226X_MAX_CHANNELS; i++) {
+			if(plat->fxs_tdm_ch[i] < 0)
+				continue;
+
+			list_for_each_entry(tdm_list_item, &slic_dev_list, list) {
+				struct tdm_device *tdm_dev = to_tdm_device(tdm_list_item->dev);
+
+				if (tdm_list_item->type != TDM_DEVICE)
+					continue;
+
+				/* Match device tdm channel with corresponding fxs port */
+				if (plat->fxs_tdm_ch[i] == tdm_dev->tdm_channel_num) {
+					found_tdm_devices[i] = to_tdm_device(tdm_list_item->dev);
+					count_found_tdm++;
+				}
+			}
+		}
+
+		/* if found needed tdm devices for current slic */
+		if (count_fxs != count_found_tdm) {
+			dev_dbg(&pdev->dev, "tdm devices not found\n");
+			return 1;
+		}
+
+		*slic = alloc_slic(pdev);
+		if (!*slic) {
+			dev_err(&pdev->dev, "can't alloc slic\n");
+			return -ENOMEM;
+		}
+
+		(*slic)->spi_dev = found_spi_dev;
+		(*slic)->reset_gpio = plat->reset_gpio;
+		(*slic)->int_gpio = plat->int_gpio;
+
+
+		for (i = 0, slic_line = (*slic)->lines;
+		     i < SI3226X_MAX_CHANNELS; i++, slic_line++) {
+			slic_line->tdm_dev = found_tdm_devices[i];
+
+			/* Remove devices from list slic devices */
+			remove_from_slic_devices_list(&found_tdm_devices[i]->dev);
+		}
+
+		/* Remove devices from list slic devices */
+		remove_from_slic_devices_list(&found_spi_dev->dev);
+		remove_from_slic_devices_list(plat_list_item->dev);
+
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/*
+ * probe spi driver
+ */
+static int probe_spi_slic(struct spi_device *spi_dev)
+{
+	struct si3226x_slic *slic;
+	int rc;
+
+	dev_dbg(&spi_dev->dev, "probe\n");
+
+	rc = add_to_slic_devices_list(&spi_dev->dev, SPI_DEVICE);
+	if (rc < 0) {
+		dev_err(&spi_dev->dev, "can't added spi device to slic list devices: %d\n", rc);
+		goto out;
+	}
+
+	rc = slic_match_bus_devices(&slic);
+	if (rc < 0) {
+		dev_err(&spi_dev->dev, "Error: %d\n", rc);
+		goto out;
+	}
+
+	if (rc) {
+		rc = 0;
+		goto out;
+	}
+
+	rc = init_slic_drv(slic);
+
+	if (rc)
+		free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+/*
+ * probe tdm driver
+ */
+static int probe_tdm_slic(struct tdm_device *tdm_dev)
+{
+	struct si3226x_slic *slic;
+	int rc;
+
+	dev_dbg(&tdm_dev->dev, "probe\n");
+
+	mutex_lock(&dev_list_lock);
+
+	rc = add_to_slic_devices_list(&tdm_dev->dev, TDM_DEVICE);
+	if (rc < 0) {
+		dev_err(&tdm_dev->dev, "can't added tdm device to slic list devices: %d\n", rc);
+		goto out;
+	}
+
+	rc = slic_match_bus_devices(&slic);
+	if (rc < 0) {
+		dev_err(&tdm_dev->dev, "Error: %d\n", rc);
+		goto out;
+	}
+
+	if (rc) {
+		rc = 0;
+		goto out;
+	}
+
+	rc = init_slic_drv(slic);
+
+	if (rc)
+		free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+/*
+ * probe slic platform driver
+ */
+static int probe_slic(struct platform_device *pdev)
+{
+	struct si3226x_slic *slic;
+	int rc;
+
+	dev_dbg(&pdev->dev, "probe\n");
+
+	mutex_lock(&dev_list_lock);
+
+	rc = add_to_slic_devices_list(&pdev->dev, PLAT_DEVICE);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "can't added platform device to slic list devices: %d\n", rc);
+		goto out;
+	}
+
+	rc = slic_match_bus_devices(&slic);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Error: %d\n", rc);
+		goto out;
+	}
+
+	if (rc) {
+		rc = 0;
+		goto out;
+	}
+
+	rc = init_slic_drv(slic);
+
+	if (rc)
+		free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+
+/*
+ * called when remove spi device
+ */
+static int __exit remove_spi_slic(struct spi_device *spi_dev)
+{
+	struct si3226x_slic *slic = NULL;
+	struct slic_chr_dev *chr_dev;
+	int rc = 0;
+
+	mutex_lock(&dev_list_lock);
+
+	/* find slic device supported current spi device */
+	list_for_each_entry(chr_dev, &slic_chr_dev_list, list) {
+		struct si3226x_slic *p;
+		p = dev_get_drvdata(chr_dev->dev);
+
+		if (p->spi_dev == spi_dev) {
+			slic = p;
+			break;
+		}
+	}
+
+	/* If line device not found in character devices list */
+	if (!slic) {
+		remove_from_slic_devices_list(&spi_dev->dev);
+		goto out;
+	}
+
+	release_slic_drv(slic);
+	rc = free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+/*
+ * remove tdm driver
+ */
+static int __exit remove_tdm_slic(struct tdm_device *tdm_dev)
+{
+	struct si3226x_slic *slic = NULL;
+	struct si3226x_line *line = NULL;
+	struct slic_chr_dev *chr_dev;
+	int rc = 0;
+
+	mutex_lock(&dev_list_lock);
+
+	/* find slic device supported current tdm device */
+	list_for_each_entry(chr_dev, &slic_chr_dev_list, list) {
+		struct si3226x_line *p;
+		p = dev_get_drvdata(chr_dev->dev);
+
+		if (p->tdm_dev == tdm_dev) {
+			line = p;
+			break;
+		}
+	}
+
+	/* If line device not found in character devices list */
+	if (!line) {
+		remove_from_slic_devices_list(&tdm_dev->dev);
+		goto out;
+	}
+
+	slic = to_si3226x_slic(line);
+
+	release_slic_drv(slic);
+	rc = free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+/*
+ * remove slic platform driver
+ */
+static int __exit remove_slic(struct platform_device *pdev)
+{
+	struct si3226x_slic *slic = NULL;
+	struct slic_chr_dev *chr_dev;
+	int rc = 0;
+
+	mutex_lock(&dev_list_lock);
+
+	/* find slic device supported current tdm device */
+	list_for_each_entry(chr_dev, &slic_chr_dev_list, list) {
+		struct si3226x_slic *p;
+		p = dev_get_drvdata(&pdev->dev);
+
+		if (p->pdev == pdev) {
+			slic = p;
+			break;
+		}
+	}
+
+	/* If line device not found in character devices list */
+	if (!slic) {
+		remove_from_slic_devices_list(&pdev->dev);
+		goto out;
+	}
+
+	release_slic_drv(slic);
+	rc = free_slic(slic);
+
+out:
+	mutex_unlock(&dev_list_lock);
+	return rc;
+}
+
+
+/*
+ * spi interface
+ */
+static struct spi_driver spi_slic_driver = {
+	.driver =
+	{
+		.name = "si3226x",
+		.bus    = &spi_bus_type,
+		.owner    = THIS_MODULE,
+	},
+	.probe    = probe_spi_slic,
+	.remove    = __exit_p(remove_spi_slic),
+};
+
+
+/*
+ * tdm Interface
+ */
+static struct tdm_driver tdm_slic_driver = {
+	.driver =
+	{
+		.name = "si3226x",
+		.bus    = &tdm_bus_type,
+		.owner    = THIS_MODULE,
+	},
+	.probe    = probe_tdm_slic,
+	.remove    = __exit_p(remove_tdm_slic),
+};
+
+
+/*
+ * slic platform interface
+ */
+static struct platform_driver slic_driver = {
+	.driver =
+	{
+		.name = "si3226x",
+		.bus    = &platform_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe	= probe_slic,
+	.remove	= __exit_p(remove_slic),
+};
+
+
+/*
+ * init kernel module
+ */
+static int __init si3226x_mod_init(void)
+{
+	int rc;
+
+	slic_class = class_create(THIS_MODULE, "slic");
+
+	rc = spi_register_driver(&spi_slic_driver);
+	if (rc) {
+		pr_debug("failed to register SPI for SLIC, error: %d\n", rc);
+		goto out0;
+	}
+
+
+	rc = tdm_register_driver(&tdm_slic_driver);
+	if (rc) {
+		pr_debug("failed to register TDM for SLIC, error: %d\n", rc);
+		goto out1;
+	}
+
+
+	rc = platform_driver_register(&slic_driver);
+	if (rc) {
+		pr_debug("failed to register SLIC on platform, error: %d\n", rc);
+		goto out2;
+	}
+
+	return 0;
+
+out2:
+	tdm_unregister_driver(&tdm_slic_driver);
+
+out1:
+	spi_unregister_driver(&spi_slic_driver);
+
+out0:
+	return rc;
+}
+
+
+/*
+ * deinit kernel module
+ */
+static void __exit si3226x_mod_exit(void)
+{
+	tdm_unregister_driver(&tdm_slic_driver);
+	spi_unregister_driver(&spi_slic_driver);
+	platform_driver_unregister(&slic_driver);
+}
+
+module_init(si3226x_mod_init);
+module_exit(si3226x_mod_exit);
+MODULE_AUTHOR("Kurochkin Michail");
+MODULE_LICENSE("GPL");
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_drv.h linux-3.8-rc5/drivers/staging/si3226x/si3226x_drv.h
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_drv.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_drv.h	2012-11-15 16:20:36.000000000 +0300
@@ -0,0 +1,181 @@
+/*
+ * si3226x_drv.h
+ * Driver SLIC Silabs si3226x
+ *
+ *  Created on: 01.03.2012
+ *      Author: Michail Kurochkin
+ */
+
+#ifndef SI3226X_H_
+#define SI3226X_H_
+
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/tdm/tdm.h>
+#include <linux/slic_si3226x.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include "fifo.h"
+
+
+#define DRIVER_NAME "si3226x"
+
+/**
+ * Calculate jiffies value for miliseconds
+ * @param ms - time in miliseconds
+ */
+#define MSEC(ms) msecs_to_jiffies(ms)
+
+#define RESET_SLIC_PERIOD 100 /*  100ms */
+#define DTMF_DIGIT_PERIOD 70 /*  DTMF tone delay */
+#define INCOMING_DTMF_BUF_SIZE 10 /*  Buffer size for incommint DTMF digits */
+#define CALLERID_BUF_SIZE 128 /*  buffer size for FSK caller id information */
+#define CALIBRATE_TIMEOUT 5000 /*  5s */
+#define ACCESS_TIMEOUT MSEC(1000) /*  register access timeout */
+#define UNLOCK_ATTEMPT_CNT 3 /*  Count retry for lock/unlock slic */
+
+#define SI3226X_MAJOR 40
+/*
+ * Caller ID modes
+ */
+enum callerid_modes
+{
+	SI_FSK_BELLCORE,
+	SI_FSK_ETSI,
+	SI_CALLERID_DTMF,
+	SI_CALLERID_NONE,
+};
+
+
+/*
+ * high level line states
+ */
+enum line_states
+{
+	SI_LINE_DISABLE, /*  disable line and voltage */
+	SI_LINE_SILENCE, /*  hook on */
+	SI_LINE_WAIT, /*  generate long tone signals */
+	SI_LINE_INVITATION, /*  generate permanent tone to line */
+	SI_LINE_BUSY, /*  generate short tone signals to line */
+	SI_LINE_TALK, /*  voice transaction, audio enable */
+};
+
+/*
+ * current telephone hook states
+ */
+enum hook_states
+{
+	SI_HOOK_ON,
+	SI_HOOK_OFF,
+};
+
+/*
+ * si3226x line structure
+ */
+struct si3226x_line
+{
+	u8 ch; /*  number of fxs channel */
+
+	dev_t devt; /*  major/minor number of line device */
+
+	enum line_states state; /*  current line state */
+	enum hook_states hook_state; /*  current telephone hook state */
+
+	enum companding_modes companding_mode; /*  a-law or m-law */
+
+	/*  audio buffers */
+	u8 *tx_buffer; /*  private transmit buffer */
+	u8 *rx_buffer; /*  private receive buffer */
+	int audio_buffer_size; /*  audio buffer size */
+
+	wait_queue_head_t *rx_wait_queue;
+	wait_queue_head_t *tx_wait_queue;
+
+	struct tdm_device *tdm_dev; /*  tdm device for transfer audio data */
+
+	u8 file_opened : 1;  /*  flag indicate opened device file */
+	u8 audio_start_flag;  /*  flag indicate audio start on line */
+
+	enum callerid_modes callerid_mode; /*  SI_CALLERID_DTMF, SI_FSK_BELLCORE, SI_FSK_ETSI */
+	struct work_struct line_call_work; /*  work queue for line call and caller_id routine */
+	u8 caller_id_buf[255]; /*  caller_id data buffer for send in line */
+	int caller_id_buf_size; /*  caller_id size of data buffer */
+
+	struct fifo_buffer dtmf; /* buffer for incomming all DTMF digits */
+	struct fifo_buffer fsk; /* buffer for transmit fsk */
+};
+
+
+/*
+ * si3226x controller structure
+ */
+struct si3226x_slic
+{
+	dev_t devt;
+
+	struct platform_device *pdev;
+	struct spi_device *spi_dev;
+	struct si3226x_line lines[SI3226X_MAX_CHANNELS];
+
+	int irq; /*  IRQ number for slic */
+	int reset_gpio; /*  GPIO for reset slic */
+	int int_gpio; /*  GPIO interrupt for slic */
+	struct work_struct irq_work;
+
+#ifdef CONFIG_SI3226X_POLLING
+	struct delayed_work delayed_work;
+#endif
+
+	u8 file_opened : 1;  /*  flag indicate opened device file */
+};
+
+
+/*
+ * slic device types
+ */
+enum slic_dev_type
+{
+	PLAT_DEVICE,
+	SPI_DEVICE,
+	TDM_DEVICE,
+};
+
+/*
+ * container for slic list probed devices
+ */
+struct slic_dev_list
+{
+	struct list_head list;
+	struct device *dev;
+	enum slic_dev_type type;
+};
+
+/*
+ * device data types
+ */
+enum chr_dev_type
+{
+	SLIC_CHR_DEV, /*  type si3226x_slic */
+	LINE_CHR_DEV, /*  type si3226x_line */
+};
+
+/*
+ * container for list registred character devices
+ */
+struct slic_chr_dev
+{
+	struct list_head list;
+	struct device *dev; /*  character device; */
+	enum chr_dev_type type; /*  character device type; */
+};
+
+
+static inline struct si3226x_slic *to_si3226x_slic(struct si3226x_line *line)
+{
+	return line ? container_of(line - line->ch, struct si3226x_slic, lines[0]) : NULL;
+}
+
+void release_slic_drv(struct si3226x_slic *slic);
+int free_slic(struct si3226x_slic *slic);
+
+#endif /* SI3226X_H_ */
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_hw.c linux-3.8-rc5/drivers/staging/si3226x/si3226x_hw.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_hw.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_hw.c	2013-01-30 15:31:09.767074000 +0300
@@ -0,0 +1,1613 @@
+/**********************************************************************
+ * Author: Michail Kurachkin
+ *
+ * Contact: michail.kurachkin@promwad.com
+ *
+ * Copyright (c) 2013 Promwad Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+**********************************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slic_si3226x.h>
+#include <linux/tdm/tdm.h>
+#include "si3226x_drv.h"
+#include "si3226x_hw.h"
+#include "fifo.h"
+#include <linux/mutex.h>
+
+/*  Include patch for si3226x */
+#include "si3226x_patch_C_FB_2011MAY19.c"
+
+/*  Include generated early setup file */
+#include "si3226x_setup.c"
+
+/*  Include generated dtmf digit table */
+#include "slic_dtmf_table.c"
+
+static DEFINE_MUTEX(spi_add_lock);
+
+
+/*
+ * si3226x register configurations for line tones
+ */
+struct si3226x_timer_regs slic_tones[] = {
+	{   /*  signal invitation */
+		.osc_amp = 0x784000,
+		.osc_freq = 0x78F0000,
+		.o_talo = 0,
+		.o_tahi = 0,
+		.o_tilo = 0,
+		.o_tihi = 0,
+	},
+	{   /*  signal busy */
+		.osc_amp = 0x784000,
+		.osc_freq = 0x78F0000,
+		.o_talo = 0xF0,
+		.o_tahi = 0xA,
+		.o_tilo = 0xF0,
+		.o_tihi = 0xA,
+	},
+	{   /*  signal wait */
+		.osc_amp = 0x7FC000,
+		.osc_freq = 0x7810000,
+		.o_talo = 0xE0,
+		.o_tahi = 0x2E,
+		.o_tilo = 0x0,
+		.o_tihi = 0x7D,
+	},
+};
+
+/**
+ * convert heximal dtmf code to ASCII code
+ * @param dtmf - heximal dtmf code
+ * @return ASCII code
+ */
+static char conv_dtmf_to_char(u8 dtmf)
+{
+	return dtmf_codes[dtmf];
+}
+
+/**
+ * ASCII code to convert heximal dtmf code
+ * @param �� - ASCII code
+ * @return heximal dtmf code or -EINVAL
+ */
+static int conv_char_to_dtmf(char ch)
+{
+	int i;
+
+	for(i = 0; i < sizeof dtmf_codes - 1; i++)
+		if(dtmf_codes[i] == ch)
+			return i;
+
+	return -EINVAL;
+}
+
+
+/**
+ * Reset SLIC
+ */
+static int slic_reset(struct si3226x_slic *slic)
+{
+	unsigned long timeout;
+
+	gpio_set_value(slic->reset_gpio, 0);
+
+	timeout = jiffies + MSEC(RESET_SLIC_PERIOD);
+
+	while(!(time_after(jiffies, timeout)))
+		schedule();
+
+	gpio_set_value(slic->reset_gpio, 1);
+
+	return 0;
+}
+
+
+/**
+ * Write value into SLIC register
+ * @param channel - channel number
+ * @param addr - address SLIC register
+ * @param data - register value
+ * @return
+ * 		0 - OK
+ * 		1 - ERROR
+ */
+int slic_write_reg(struct si3226x_line *line, u8 addr, u8 data)
+{
+	u8 ch;
+	int rc;
+	int i;
+	struct si3226x_slic *slic = to_si3226x_slic(line);
+
+	mutex_lock(&spi_add_lock);
+
+	if (line && line->ch >= SI3226X_MAX_CHANNELS) {
+		mutex_unlock(&spi_add_lock);
+		dev_err(&line->tdm_dev->dev, "Incorrect slic line number\n");
+		return -EINVAL;
+	}
+
+	if(line == NULL)
+		ch = 0;
+	else
+		ch = line->ch;
+
+
+	/**
+	 * bit 7 - BRDCST - Indicates a broadcast operation that is intended
+	 * for all devices in the daisy chain. This is
+	 * only valid for write operations since it would cause contention
+	 * on the SDO pin during a read.
+		bit 6 - R/W - Read/Write Bit.
+			0 = Write operation.
+			1 = Read operation.
+		bit 5 - REG/RAM - Register/RAM Access Bit.
+			0 = RAM access.
+			1 = Register access.
+		bit 4:0 -  CID[4:0] - Indicates the channel that is targeted by
+		 	the operation. Note that the 4-bit channel value is
+		 	provided LSB first. The devices reside on the daisy
+		 	chain such that device 0 is nearest to the controller,
+		 	and device 15 is furthest down the SDI/SDU_THRU chain.
+		 	(See Figure 41.)
+			As the CID information propagates down the daisy chain,
+			each channel decrements the CID by 1. The SDI nodes
+			between devices reflect a decrement of 2 per device
+			since each device contains two channels. The device
+			receiving a value of 0 in the CID field responds
+			to the SPI transaction. (See Figure 42.) If a broadcast
+			to all devices connected to the chain is requested,
+			the CID does not  decrement. In this case, the same
+			8-bit or 16-bit data is pre-sented to all channels
+			regardless of the CID values.
+	 */
+	{
+
+		u8 write_transaction[] = {
+			(1 << 5) | ((ch & 1) << 4), /*  control byte */
+			addr,
+			data,
+		};
+
+		for(i = 0; i < sizeof write_transaction; i++) {
+			rc = spi_write(slic->spi_dev, write_transaction + i, 1);
+			if (rc) {
+				mutex_unlock(&spi_add_lock);
+				return rc;
+			}
+		}
+	}
+
+	mutex_unlock(&spi_add_lock);
+	return rc;
+}
+
+
+/**
+ * Read value from SLIC register
+ * @param line - line descriptor or NULL if read GLOBAL register
+ * @param addr - address SLIC register
+ * @param data - pointer to read data
+ * @return 0 - ok
+ */
+static int slic_read_reg(struct si3226x_line *line, u8 addr, u8 *data)
+{
+	int rc;
+	u8 ch;
+	int i;
+	struct si3226x_slic *slic = to_si3226x_slic(line);
+
+	mutex_lock(&spi_add_lock);
+
+	if (line && line->ch >= SI3226X_MAX_CHANNELS) {
+		mutex_unlock(&spi_add_lock);
+		return -EINVAL;
+	}
+
+	ch = line->ch;
+
+	{
+		u8 read_transaction[] = {
+			(1 << 5) | (1 << 6) | ((ch & 1) << 4), /*  control byte */
+			addr,
+		};
+
+		for(i = 0; i < sizeof read_transaction; i++) {
+			rc = spi_write(slic->spi_dev, read_transaction + i, 1);
+			if (rc) {
+				mutex_unlock(&spi_add_lock);
+				return rc;
+			}
+		}
+	}
+
+	rc = spi_read(slic->spi_dev, data, 1);
+
+	mutex_unlock(&spi_add_lock);
+	return rc;
+}
+
+
+#ifdef DEBUG
+/**
+ * Read value from SLIC RAM
+ * @param channel - channel number
+ * @param addr - address SLIC RAM
+ * @return value 29bit of 32bit
+ */
+static int slic_read_ram(struct si3226x_line *line, u16 addr, u32 *value)
+{
+	int rc;
+	u8 *val = (u8 *)value;
+	unsigned long timeout;
+	u8 state;
+
+	rc = slic_write_reg(line, SI_RAM_ADDR_HI, (addr >> 3) & 0xe0);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_ADDR_LO, addr & 0xFF);
+	if (rc)
+		return rc;
+
+	timeout = jiffies + ACCESS_TIMEOUT;
+	do {
+		rc = slic_read_reg(line, SI_REG_RAMSTAT, &state);
+		if (rc)
+			return rc;
+
+		state &= 1;
+		if(time_after(jiffies, timeout)) {
+			dev_err(&line->tdm_dev->dev,
+			        "Can`t read access to slic RAM\n");
+			return -EBUSY;
+		}
+	} while(state);
+
+	rc = slic_read_reg(line, SI_RAM_DATA_B0, val + 0);
+	if (rc)
+		return rc;
+
+	rc = slic_read_reg(line, SI_RAM_DATA_B1, val + 1);
+	if (rc)
+		return rc;
+
+	rc = slic_read_reg(line, SI_RAM_DATA_B2, val + 2);
+	if (rc)
+		return rc;
+
+	rc = slic_read_reg(line, SI_RAM_DATA_B3, val + 3);
+	if (rc)
+		return rc;
+
+	*value >>= 3;
+
+	return 0;
+}
+#endif
+
+
+/**
+ * Write value into SLIC RAM
+ * @param channel - channel number
+ * @param addr - address SLIC RAM
+ * @param value - value of SLIC RAM
+ * @return
+ *
+ */
+int slic_write_ram(struct si3226x_line *line, u16 addr, u32 value)
+{
+	int rc;
+	unsigned long timeout;
+	u8 state;
+	u8 *write_data;
+	u32 val = value << 3;
+
+	write_data = (u8 *)&val;
+
+	if((!line) || line->ch >= SI3226X_MAX_CHANNELS)
+		return -EINVAL;
+
+	value <<= 3;
+
+	rc = slic_write_reg(line, SI_RAM_ADDR_HI, (addr >> 3) & 0xe0);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_DATA_B0,  write_data[0] & 0xF8);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_DATA_B1,  write_data[1]);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_DATA_B2,  write_data[2]);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_DATA_B3,  write_data[3]);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_RAM_ADDR_LO, addr & 0xFF);
+	if (rc)
+		return rc;
+
+	timeout = jiffies + ACCESS_TIMEOUT;
+	do {
+		rc = slic_read_reg(line, SI_REG_RAMSTAT, &state);
+		if (rc)
+			return rc;
+
+		state &= 1;
+		if(time_after(jiffies, timeout)) {
+			dev_err(&line->tdm_dev->dev, "Can`t write access to slic RAM\n");
+			return -EBUSY;
+		}
+	} while (state);
+
+	return 0;
+}
+
+
+
+/**
+ * Lock channel. To protect against undersirable/unintended
+ * system operation, there are certain critical register control
+ * bits that are protected by default against unintentional
+ * modification.
+ * @param channel - channel number
+ * @return 0 - ok
+ */
+int slic_lock_channel(struct si3226x_line *line)
+{
+	int rc;
+	int i;
+	u8 state;
+	int attempt_cnt = UNLOCK_ATTEMPT_CNT;
+	u8 unlock_data[] = {0x2, 0x8, 0xE, 0};
+
+	do {
+		rc = slic_read_reg(line, SI_REG_UAM, &state);
+		if (rc)
+			return rc;
+
+		if((state & 1) == 0) /*  if channel already locked */
+			return 0;
+
+		for(i = 0; i < sizeof unlock_data; i++) {
+			rc = slic_write_reg(line, SI_REG_UAM, unlock_data[i]);
+			if (rc)
+				return rc;
+		}
+
+		rc = slic_read_reg(line, SI_REG_UAM, &state);
+		if (rc)
+			return rc;
+
+		attempt_cnt--;
+	} while ((state & 1) && attempt_cnt);
+
+	if (attempt_cnt == 0) {
+		dev_err(&line->tdm_dev->dev,
+		        "can`t lock slic channel %d\n", line->ch);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Unlock channel. While in protected mode, any writes to
+ * protected register bits or protected RAM space are simply
+ * ignored by the si3226x.
+ * @param channel - channel number
+ * @return 0 - ok
+ */
+int slic_unlock_channel(struct si3226x_line *line)
+{
+	int rc;
+	int i;
+	u8 state;
+	int attempt_cnt = UNLOCK_ATTEMPT_CNT;
+
+	/* unlock data consecution */
+	u8 unlock_data[] = {0x2, 0x8, 0xE, 0};
+
+	do {
+		rc = slic_read_reg(line, SI_REG_UAM, &state);
+		if (rc)
+			return rc;
+
+		if((state & 1) != 0) /*  if channel already unlocked */
+			return 0;
+
+		for(i = 0; i < sizeof unlock_data; i++) {
+			rc = slic_write_reg(line, SI_REG_UAM, unlock_data[i]);
+			if (rc)
+				return rc;
+		}
+
+		rc = slic_read_reg(line, SI_REG_UAM, &state);
+		if (rc)
+			return rc;
+
+		attempt_cnt--;
+	} while ((!(state & 1)) && attempt_cnt);
+
+	if (attempt_cnt == 0) {
+		dev_err(&line->tdm_dev->dev,
+		        "can`t unlock slic channel %d\n", line->ch);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Calibrate slic
+ * @param slic - slic descriptor
+ * @return 0 - ok
+ */
+int slic_calibrate(struct si3226x_line *line)
+{
+	int rc;
+	u8 data;
+	unsigned long timeout;
+
+	rc = slic_write_reg(line, SI_REG_CALR3, SI_VAL_CAL_EN);
+	if (rc)
+		return rc;
+
+	timeout = jiffies + MSEC(CALIBRATE_TIMEOUT);
+	do {
+		rc = slic_read_reg(line, SI_REG_CALR3, &data);
+		if (rc)
+			return rc;
+
+		data &= SI_VAL_CAL_EN;
+
+		if(time_after(jiffies, timeout)) {
+			dev_err(&line->tdm_dev->dev,
+			        "Can`t calibrate slic line %d\n",
+			        line->ch);
+			return -EBUSY;
+		}
+	} while(data);
+
+
+	return 0;
+}
+
+
+/**
+ * Enable hardware echo cancelation
+ * @param line - line
+ * @return 0 - ok
+ */
+int slic_enable_echo(struct si3226x_line *line)
+{
+	int rc;
+	u8 data;
+
+	rc = slic_read_reg(line, SI_REG_DIGCON, &data);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_DIGCON, data & (~SI_VAL_HYB_DIS));
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+
+/**
+ * Disable hardware echo cancelation
+ * @param line - line descriptor
+ * @return 0 - ok
+ */
+int slic_disable_echo(struct si3226x_line *line)
+{
+	int rc;
+	u8 data;
+
+	rc = slic_read_reg(line, SI_REG_DIGCON, &data);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_DIGCON, data | SI_VAL_HYB_DIS);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+
+/**
+ * function send FSK data from fifo to FXS line.
+ * called by irq when hardware fsk buffer is empty
+ * @param line - line descriptor
+ */
+static void do_fsk(struct si3226x_line *line)
+{
+	u8 data;
+	int rc;
+
+	/*  put 8 byte into hardware fsk fifo */
+	rc = fifo_pop(&line->fsk, &data);
+	if (rc) {
+		mdelay(300);
+		slic_write_reg(line, SI_REG_OCON, 0);
+		return;
+	}
+
+	slic_write_reg(line, SI_REG_FSKDAT, data);
+}
+
+
+/**
+ * Run sending fsk caller id procedure.
+ * asynchronous send fsk data by irq
+ * @param line - line descriptor
+ * @param mode - type of caller id. Declared in enum callerid_modes
+ * @param buf - caller id data
+ * @param size - caller id size
+ * @return 0 - ok
+ */
+static int
+slic_send_fsk(struct si3226x_line *line, int mode, u8 *buf, int size)
+{
+	int rc;
+	int i;
+	u8 data;
+
+	if (!size)
+		return -EINVAL;
+
+	if (!fifo_is_empty(&line->fsk)) {
+		rc = init_fifo(&line->fsk, 1, CALLERID_BUF_SIZE);
+		if (rc)
+			return rc;
+	}
+
+	data = 0x55;
+	for (i = 0; i < 38; i++)
+		fifo_push(&line->fsk, &data);
+
+	data = 0xFF;
+	for (i = 0; i < 19; i++)
+		fifo_push(&line->fsk, &data);
+
+	for (i = 0; i < size; i++)
+		fifo_push(&line->fsk, buf + i);
+
+	rc = slic_write_reg(line, SI_REG_OMODE,
+	                    SI_VAL_OSC1_FSK | SI_VAL_ROUTING_1_2);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_FSKDEPTH, SI_VAL_FSK_FLUSH);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_FSKDEPTH, 0);
+	if (rc)
+		return rc;
+
+
+	switch (mode) {
+	case SI_FSK_BELLCORE:
+		rc = slic_write_ram(line, SI_RAM_FSKAMP0, 0x105E000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKAMP1, 0x8BE000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKFREQ0, 0x6B60000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKFREQ1, 0x79C0000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSK01, 0x2232000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSK10, 0x77C2000);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_FSK_ETSI:
+		rc = slic_write_ram(line, SI_RAM_FSKAMP0, 0x340000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKAMP1, 0x1FA000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKFREQ0, 0x6D20000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSKFREQ1, 0x78B0000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSK01, 0x26E4000);
+		if (rc)
+			return rc;
+
+		rc = slic_write_ram(line, SI_RAM_FSK10, 0x694C000);
+		if (rc)
+			return rc;
+
+		break;
+	}
+
+	rc = slic_write_reg(line, SI_REG_O1TALO, 0x14);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_O1TAHI, 0);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_OCON, SI_VAL_OSC1_EN | SI_VAL_OSC1_TA_EN);
+	if (rc)
+		return rc;
+
+	do_fsk(line);
+	return 0;
+}
+
+
+/**
+ * Disable DTMF recognizing on FXS line
+ * @param line
+ * @return 0 - ok
+ */
+static int disable_dtmf_detect(struct si3226x_line *line)
+{
+	return slic_write_reg(line, SI_REG_TONEN, 0xe3);
+}
+
+/**
+ * Enable DTMF recognizing on FXS line
+ * @param line
+ * @return 0 - ok
+ */
+static int enable_dtmf_detect(struct si3226x_line *line)
+{
+	return slic_write_reg(line, SI_REG_TONEN, 0xe0);
+}
+
+/**
+ * Send callerID data
+ * @param line - line
+ * @param buf - callerID buffer
+ * @param size - buffer size
+ * @return 0 - ok
+ */
+static int slic_send_callerid(struct si3226x_line *line, char *buf, int size)
+{
+	int rc;
+	int i;
+
+	switch(line->callerid_mode) {
+	case SI_CALLERID_DTMF:
+		rc = disable_dtmf_detect(line);
+		if (rc)
+			return rc;
+
+		for(i = 0; i < size; i++) {
+			rc = slic_send_dtmf_digit(line, buf[i]);
+
+			if(rc || line->hook_state)
+				return enable_dtmf_detect(line);
+
+			msleep(70);
+		}
+
+		rc = enable_dtmf_detect(line);
+		if (rc)
+			return rc;
+
+		break;
+
+
+	case SI_FSK_BELLCORE:
+		rc = slic_send_fsk(line, SI_FSK_BELLCORE, buf, size);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_FSK_ETSI:
+		rc = slic_send_fsk(line, SI_FSK_ETSI, buf, size);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_CALLERID_NONE:
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * Set hardware slic channel state
+ * @param line - line descriptor
+ * @param state - hardware line state
+ * @return 0 - ok
+ */
+static int
+slic_set_linefeed_state(struct si3226x_line *line, enum fxs_states state)
+{
+	int rc;
+
+	rc = slic_write_reg(line, SI_REG_LINEFEED, state);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+
+/**
+ * send ring signal to line and send callerid if not null.
+ * Functon is blocking while sending callerid
+ * @param line - line descriptor
+ * @param callerid_buf - buffer witch callerid
+ * @param size - caller id buffer size
+ * @return 0 - ok
+ */
+int slic_line_call(struct si3226x_line *line, u8 *callerid_buf, int size)
+{
+	int stored_size;
+
+	stored_size = size > (sizeof(line->caller_id_buf) - 1) ?
+	              (sizeof(line->caller_id_buf) - 1) : size;
+
+	memcpy(line->caller_id_buf, callerid_buf, stored_size);
+	line->caller_id_buf_size = stored_size;
+
+	schedule_work(&line->line_call_work);
+
+	return 0;
+}
+
+/**
+ * Work queue IRQ callback handled all slic events
+ * @param work - work queue item
+ */
+void do_line_call(struct work_struct *work)
+{
+	struct si3226x_line *line = container_of(work, struct si3226x_line, line_call_work);
+
+	u8 data;
+	int rc;
+
+	rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_OHT);
+	if (rc)
+		return;
+
+	rc = slic_set_linefeed_state(line, SI_FXS_RINGING);
+	if (rc)
+		return;
+
+	if(line->callerid_mode == SI_CALLERID_NONE)
+		return;
+
+	msleep(30);
+
+	do {
+		rc = slic_read_reg(line, SI_REG_LINEFEED, &data);
+		if (rc)
+			return;
+
+		schedule();
+	} while (data & (1 << 6));
+
+	msleep(200);
+
+	rc = slic_send_callerid(line, line->caller_id_buf, line->caller_id_buf_size);
+	if (rc)
+		return;
+}
+
+/**
+ * Setup slic hardware timer
+ * @param line - line descriptor
+ * @param timer_num - number of timer
+ * @param timer_regs - list specified timer registers values
+ * @return 0 - ok
+ */
+static int
+slic_set_timer(struct si3226x_line *line, u8 timer_num,
+               struct si3226x_timer_regs *timer_regs)
+{
+	int rc;
+
+	if(timer_num > 1)
+		return -EINVAL;
+
+	rc = slic_write_ram(line, SI_RAM_OSC1AMP + timer_num * 3,
+	                    timer_regs->osc_amp);
+	if (rc)
+		return rc;
+
+	rc = slic_write_ram(line, SI_RAM_OSC1FREQ + timer_num * 3,
+	                    timer_regs->osc_freq);
+	if (rc)
+		return rc;
+
+	rc = slic_write_ram(line, SI_RAM_OSC1PHAS + timer_num * 3, 0);
+	if (rc)
+		return rc;
+
+	/*  configure timer delay */
+	rc = slic_write_reg(line, SI_REG_O1TALO + timer_num * 4,
+	                    timer_regs->o_talo);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_O1TAHI + timer_num * 4,
+	                    timer_regs->o_tahi);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_O1TILO + timer_num * 4,
+	                    timer_regs->o_tilo);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_O1TIHI + timer_num * 4,
+	                    timer_regs->o_tihi);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+
+/**
+ * Send tone signal to line
+ * @param line - line descriptor
+ * @param type - type of signal (defined in array slic_tones[])
+ * @return 0 - ok
+ */
+static int
+slic_set_signal_to_line(struct si3226x_line *line, enum tone_types type)
+{
+	int rc;
+
+	if (type > ARRAY_SIZE(slic_tones))
+		return -EINVAL;
+
+	/*  disable tone if needed */
+	if(type == SI_TONE_NONE) {
+		rc = slic_write_reg(line, SI_REG_OCON, 0);
+		return rc;
+	}
+
+	rc = slic_set_timer(line, 0, slic_tones +(int)type);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_OMODE,
+	                    SI_VAL_ROUTING_1_3 | SI_VAL_ZERO_EN_1);
+	if (rc)
+		return rc;
+
+	switch(type) {
+	case SI_TONE_INVITATION:
+		rc = slic_write_reg(line, SI_REG_OCON, SI_VAL_OSC1_EN);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_TONE_BUSY:
+	case SI_TONE_WAIT:
+		rc = slic_write_reg(line, SI_REG_OCON,
+		                    SI_VAL_OSC1_EN | SI_VAL_OSC1_TA_EN | SI_VAL_OSC1_TI_EN);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_TONE_NONE:
+		break;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Set high level line state
+ * @param line - line descriptor
+ * @param state - needed line state
+ * @return 0 - ok
+ */
+int slic_set_line_state(struct si3226x_line *line, int state)
+{
+	int rc;
+
+	if (state > SI_LINE_TALK)
+		return -EINVAL;
+
+	switch(state) {
+	case SI_LINE_DISABLE:
+		rc = slic_set_linefeed_state(line, SI_FXS_OPEN);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_LINE_SILENCE:
+		rc = slic_set_signal_to_line(line, SI_TONE_NONE);
+		if (rc)
+			return rc;
+
+		rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_ACTIVE);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_LINE_INVITATION:
+		rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_ACTIVE);
+		if (rc)
+			return rc;
+
+		rc = slic_set_signal_to_line(line, SI_TONE_INVITATION);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_LINE_WAIT:
+		rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_ACTIVE);
+		if (rc)
+			return rc;
+
+		rc = slic_set_signal_to_line(line, SI_TONE_WAIT);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_LINE_BUSY:
+		rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_ACTIVE);
+		if (rc)
+			return rc;
+
+		rc = slic_set_signal_to_line(line, SI_TONE_BUSY);
+		if (rc)
+			return rc;
+
+		break;
+
+	case SI_LINE_TALK:
+		rc = slic_set_signal_to_line(line, SI_TONE_NONE);
+		if (rc)
+			return rc;
+
+		rc = slic_set_linefeed_state(line, SI_FXS_FORWARD_ACTIVE);
+		if (rc)
+			return rc;
+
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * Send one digit by DTMF
+ * @param line - line
+ * @param ch - heximal DTMF code
+ * @return 0 - ok
+ */
+int slic_send_dtmf_digit(struct si3226x_line *line, char ch)
+{
+	int rc;
+	int i;
+	unsigned long timeout;
+	u8 data;
+	int dtmf_ch;
+	struct si3226x_timer_regs timer_regs[2];
+	u16 dtmf_delay = DTMF_DIGIT_PERIOD * 1000 / 125;
+
+	memset(timer_regs, 0, sizeof timer_regs);
+	dtmf_ch = conv_char_to_dtmf(ch);
+	if (dtmf_ch < 0)
+		return dtmf_ch;
+
+	if(dtmf_ch > 0xF)
+		return -EINVAL;
+
+	timer_regs[0].osc_amp = slic_dtmf_table[dtmf_ch].osc1amp;
+	timer_regs[0].osc_freq = slic_dtmf_table[dtmf_ch].osc1freq;
+	timer_regs[0].o_talo = (u8)(dtmf_delay & 0xFF);
+	timer_regs[0].o_tahi = (u8)((dtmf_delay >> 8) & 0xFF);
+
+	timer_regs[1].osc_amp = slic_dtmf_table[dtmf_ch].osc2amp;
+	timer_regs[1].osc_freq = slic_dtmf_table[dtmf_ch].osc2freq;
+	timer_regs[1].o_talo = (u8)(dtmf_delay & 0xFF);
+	timer_regs[1].o_tahi = (u8)((dtmf_delay >> 8) & 0xFF);
+
+	for(i = 0; i < 2; i++) {
+		rc = slic_set_timer(line, i, timer_regs + i);
+		if (rc)
+			return rc;
+	}
+
+	/*  Sending tone to */
+	rc = slic_write_reg(line, SI_REG_OMODE,
+	                    SI_VAL_ROUTING_1_3 | SI_VAL_ZERO_EN_1 |
+	                    SI_VAL_ROUTING_2_3 | SI_VAL_ZERO_EN_2);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_OCON,
+	                    SI_VAL_OSC1_EN | SI_VAL_OSC1_TA_EN |
+	                    SI_VAL_OSC2_EN | SI_VAL_OSC2_TA_EN);
+	if (rc)
+		return rc;
+
+	/*  sleeping by DTMF is signalling */
+	msleep(DTMF_DIGIT_PERIOD);
+
+	timeout = jiffies + ACCESS_TIMEOUT;
+	do {
+		rc = slic_read_reg(line, SI_REG_OCON, &data);
+		if (rc)
+			return rc;
+
+		data &= (SI_VAL_EN_SYNC_1 | SI_VAL_EN_SYNC_2);
+		if(time_after(jiffies, timeout))
+			return -EBUSY;
+	} while(data);
+
+	return 0;
+}
+
+
+/**
+ * Mask irq for slic line
+ * @param line - line descriptor
+ * @return 0 - ok
+ */
+static int slic_mask_irq(struct si3226x_line *line)
+{
+	int rc;
+	u8 data;
+	int i;
+
+	/*  Cleanup all irq flags */
+	for(i = SI_REG_IRQ0; i < SI_REG_IRQ4; i++) {
+		rc = slic_read_reg(line, i, &data);
+		if (rc)
+			return rc;
+	}
+
+	/*  FSK empty buffer */
+	rc = slic_write_reg(line, SI_REG_IRQEN1, SI_VAL_FSKBUF_AVAIL_IA);
+	if (rc)
+		return rc;
+
+	/*  RX,TX modem tone detector, DTMF, on/off hook */
+	rc = slic_write_reg(line, SI_REG_IRQEN2,
+	                    SI_VAL_TXMDM_IA | SI_VAL_RXMDM_IA | SI_VAL_DTMF_IA | SI_VAL_LCR_IA);
+	if (rc)
+		return rc;
+
+	/*  Termo alarm */
+	rc = slic_write_reg(line, SI_REG_IRQEN3, SI_VAL_P_TERM_IA);
+	if (rc)
+		return rc;
+
+	/*  disable unused irq */
+	rc = slic_write_reg(line, SI_REG_IRQEN4, 0);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+
+/**
+ * Upload patch for si3226x
+ * @param line - patch upload for line
+ * @return 0 - ok
+ */
+static int upload_patch(struct si3226x_line *line)
+{
+	int rc;
+	int i;
+
+	rc = slic_write_reg(line, SI_REG_JMPEN, 0);
+	if (rc)
+		return rc;
+
+	/*  load patch ram */
+	rc = slic_write_ram(line, SI_RAM_PRAM_ADDR, 0);
+	if (rc)
+		return rc;
+
+	for(i = 0; i < ARRAY_SIZE(si3226x_patch_data); i++) {
+		rc = slic_write_ram(line, SI_RAM_PRAM_DATA,
+		                    si3226x_patch_data[i]);
+		if (rc)
+			return rc;
+	}
+
+	/*  load jump table */
+	for(i = SI_REG_JMP0LO; i < SI_REG_JMP7HI; i++) {
+		rc = slic_write_reg(line, i,
+		                    si3226x_patch_entries[i - SI_REG_JMP0LO]);
+		if (rc)
+			return rc;
+	}
+
+	/*  load RAM */
+	for(i = 0; i < ARRAY_SIZE(si3226x_patch_support_addr); i++) {
+		rc = slic_write_ram(line, si3226x_patch_support_addr[i],
+		                    si3226x_patch_support_data[i]);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Setup slic hardware audio settings
+ * @param line - line descriptor
+ * @return 0 - ok
+ */
+int slic_setup_audio(struct si3226x_line *line)
+{
+	struct tdm_device *tdm_dev = line->tdm_dev;
+	struct tdm_controller_hw_settings *tdm_controller_hw =
+			    tdm_dev->controller->settings;
+	u16 time_slot_pos =
+	    tdm_dev->tdm_channel_num * tdm_controller_hw->channel_size * 8;
+	u8 tx_edge;
+	u8 bus_format = 0;
+	int rc;
+
+	switch(line->companding_mode) {
+	case SI_ALAW:
+		bus_format = SI_VAL_PCM_FMT_0 | SI_VAL_PCM_ALAW_1;
+		break;
+
+	case SI_MLAW:
+		bus_format = SI_VAL_PCM_FMT_1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	tx_edge = (tdm_controller_hw->data_polarity == TDM_POLAR_NEGATIVE) << 4;
+
+	rc = slic_write_reg(line, SI_REG_PCMTXLO, (u8)(time_slot_pos & 0xFF));
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_PCMTXHI,
+	                    (u8)((time_slot_pos >> 8) & 0x3) | tx_edge);
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_PCMRXLO, (u8)(time_slot_pos & 0xFF));
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_PCMRXHI,
+	                    (u8)((time_slot_pos >> 8) & 0x3));
+	if (rc)
+		return rc;
+
+	rc = slic_write_reg(line, SI_REG_PCMMODE, bus_format | SI_VAL_PCM_EN);
+	if (rc)
+		return rc;
+
+	if (!line->audio_start_flag) {
+		rc = tdm_run_audio(tdm_dev);
+		if (!rc)
+			line->audio_start_flag = 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Init hardware slic line
+ * @param line - line descriptor
+ * @return 0 - ok
+ */
+static int init_line(struct si3226x_line *line)
+{
+	int rc;
+
+	dev_info(&line->tdm_dev->dev, "run init line\n");
+
+	/*  Init default Caller ID mode */
+	line->callerid_mode = SI_CALLERID_NONE;
+
+	rc = init_fifo(&line->dtmf, 1, INCOMING_DTMF_BUF_SIZE);
+	if (rc)
+		return rc;
+
+	rc = init_fifo(&line->fsk, 1, CALLERID_BUF_SIZE);
+	if (rc)
+		return rc;
+
+
+	rc = slic_setup_audio(line);
+	if (rc)
+		return rc;
+
+	rc = slic_set_line_state(line, SI_LINE_SILENCE);
+	if (rc)
+		return rc;
+
+	rc = slic_mask_irq(line);
+	if (rc)
+		return rc;
+
+	dev_info(&line->tdm_dev->dev, "init line done\n");
+
+	return 0;
+}
+
+
+/**
+ * Init hardware slic
+ * @param slic - slic descriptor
+ * @return 0 - ok
+ */
+int init_slic(struct si3226x_slic *slic)
+{
+	int rc;
+	int i;
+	struct si3226x_line *line = slic->lines;
+	struct platform_device *pdev = slic->pdev;
+	u8 chip_id;
+
+	dev_info(&pdev->dev, "run slic initialization\n");
+
+	rc = slic_reset(slic);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to reset SLIC\n");
+		return rc;
+	}
+
+	printk("slic_reset\n");
+
+	mdelay(50);
+
+	rc = slic_read_reg(line, 0, &chip_id);
+	if (rc)
+		return rc;
+
+	if (chip_id != 0xC3) {
+		dev_err(&pdev->dev, "Incorrect slic chip ID: 0x%X\n", chip_id);
+		return -ENODEV;
+	}
+
+
+
+	for (i = 0; i < SI3226X_MAX_CHANNELS; i++, line++) {
+		if (line->state == SI_LINE_DISABLE)
+			continue;
+
+		rc = slic_unlock_channel(line);
+		if (rc)
+			return rc;
+
+		rc = upload_patch(line);
+		if (rc)
+			return rc;
+
+		rc = slic_lock_channel(line);
+		if (rc)
+			return rc;
+
+		dev_info(&pdev->dev, "line: %d, patch uploaded\n", line->ch);
+	}
+
+	rc = slic_load_settings(slic);
+	if (rc)
+		return rc;
+
+	dev_info(&pdev->dev, "settings loaded\n");
+
+	for (i = 0, line = slic->lines; i < SI3226X_MAX_CHANNELS; i++, line++) {
+		if (line->state == SI_LINE_DISABLE)
+			continue;
+
+		rc = init_line(line);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+
+/**
+ * deInit hardware slic. stop slic.
+ * @param slic - slic descriptor
+ * @return 0 - ok
+ */
+int deinit_slic(struct si3226x_slic *slic)
+{
+	struct si3226x_line *line;
+	int i;
+
+	for (i = 0, line = slic->lines; i < SI3226X_MAX_CHANNELS; i++, line++) {
+		if (line->state == SI_LINE_DISABLE)
+			continue;
+
+		slic_set_line_state(line, SI_LINE_SILENCE);
+
+		free_fifo(&line->dtmf);
+		free_fifo(&line->fsk);
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * IRQ event handlers - event_xxx...
+ */
+static void event_modem_detect(struct si3226x_line *line)
+{
+
+}
+
+static void event_dtmf_detect(struct si3226x_line *line)
+{
+	u8 ch;
+	u8 data = 0;
+
+	slic_read_reg(line, SI_REG_TONDTMF, &data);
+
+	/*  if dtmf not recognized */
+	if (!(data & SI_VAL_VALID))
+		return;
+
+	ch = conv_dtmf_to_char(data & 0xF);
+
+	fifo_push(&line->dtmf, &ch);
+}
+
+static void event_hook_detect(struct si3226x_line *line)
+{
+	u8 data = 0;
+
+	slic_read_reg(line, SI_REG_LCRRTP, &data);
+
+	if (data & SI_VAL_LCR)
+		line->hook_state = 1;
+	else
+		line->hook_state = 0;
+}
+
+static void event_error_detect(struct si3226x_line *line, enum si3226x_errors err)
+{
+	struct si3226x_slic *slic = to_si3226x_slic(line);
+
+	switch(err) {
+	case SI_ERR_TERMAL_SHOCK:
+		dev_err(&slic->pdev->dev, "SLIC Termal shock! Stopped driver\n");
+		release_slic_drv(slic);
+		free_slic(slic);
+		break;
+	}
+}
+
+
+
+#ifdef CONFIG_SI3226X_POLLING
+void slic_delayed_work(struct delayed_work *work)
+{
+	struct si3226x_slic *slic =
+	    container_of(work, struct si3226x_slic, delayed_work);
+
+	slic_irq_callback(&slic->irq_work);
+
+	schedule_delayed_work(work, MSEC(50));
+}
+#endif
+
+/**
+ * Interrupt handler for slic
+ * @param irq - irq number
+ * @param context_data - slic private data
+ */
+irqreturn_t slic_irq(s32 irq, void *context_data)
+{
+	struct si3226x_slic *slic = context_data;
+	int value = gpio_get_value(slic->int_gpio);
+
+	if (value == 0)
+		schedule_work(&slic->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+
+/**
+ * Work queue IRQ callback handled all slic events
+ * @param work - work queue item
+ */
+void slic_irq_callback(struct work_struct *work)
+{
+	struct si3226x_slic *slic =
+	    container_of(work, struct si3226x_slic, irq_work);
+
+	struct si3226x_line *line = 0;
+	u8 global_irq_status;
+	u8 data;
+	int rc;
+	int i;
+
+	rc = slic_read_reg(slic->lines, SI_REG_IRQ0, &global_irq_status);
+	if (rc)
+		return;
+
+	/*  identificate irq reason */
+	for (i = 0; i < 8; i++) {
+		if (!(global_irq_status & (1 << i)))
+			continue;
+
+		switch (i) {
+		case 0:
+			line = slic->lines + 0;
+			rc = slic_read_reg(line, SI_REG_IRQ1, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_FSKBUF_AVAIL_IA)
+				do_fsk(line);
+			break;
+
+		case 1:
+			line = slic->lines + 0;
+			rc = slic_read_reg(line, SI_REG_IRQ2, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_RXMDM_IA)
+				event_modem_detect(line);
+
+			if (data & SI_VAL_TXMDM_IA)
+				event_modem_detect(line);
+
+			if (data & SI_VAL_DTMF_IA)
+				event_dtmf_detect(line);
+
+			if (data & SI_VAL_LCR_IA)
+				event_hook_detect(line);
+			break;
+
+		case 2:
+			line = slic->lines + 0;
+			rc = slic_read_reg(line, SI_REG_IRQ3, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_P_TERM_IA)
+				event_error_detect(line, SI_ERR_TERMAL_SHOCK);
+			break;
+
+		case 3:
+			line = slic->lines + 0;
+			rc = slic_read_reg(line, SI_REG_IRQ4, &data);
+			if (rc)
+				return;
+
+			break;
+
+		case 4:
+			line = slic->lines + 1;
+			rc = slic_read_reg(line, SI_REG_IRQ1, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_FSKBUF_AVAIL_IA)
+				do_fsk(line);
+			break;
+
+		case 5:
+			line = slic->lines + 1;
+			rc = slic_read_reg(line, SI_REG_IRQ2, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_RXMDM_IA)
+				event_modem_detect(line);
+
+			if (data & SI_VAL_TXMDM_IA)
+				event_modem_detect(line);
+
+			if (data & SI_VAL_DTMF_IA)
+				event_dtmf_detect(line);
+
+			if (data & SI_VAL_LCR_IA)
+				event_hook_detect(line);
+			break;
+
+		case 6:
+			line = slic->lines + 1;
+			rc = slic_read_reg(line, SI_REG_IRQ3, &data);
+			if (rc)
+				return;
+
+			if (data & SI_VAL_P_TERM_IA)
+				event_error_detect(line, 0);
+			break;
+
+		case 7:
+			line = slic->lines + 1;
+			rc = slic_read_reg(line, SI_REG_IRQ4, &data);
+			if (rc)
+				return;
+
+			break;
+		}
+	}
+}
+
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_hw.h linux-3.8-rc5/drivers/staging/si3226x/si3226x_hw.h
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_hw.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_hw.h	2013-01-28 17:54:00.000000000 +0300
@@ -0,0 +1,231 @@
+/*
+ * si3226x_hw.h
+ *
+ *  Created on: 14.03.2012
+ *      Author: Michail Kurochkin
+ */
+
+#ifndef SI3226X_HW_H_
+#define SI3226X_HW_H_
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include "si3226x_drv.h"
+
+/* define ram registers */
+#define SI_RAM_ADDR_HI 5
+#define SI_RAM_DATA_B0 6
+#define SI_RAM_DATA_B1 7
+#define SI_RAM_DATA_B2 8
+#define SI_RAM_DATA_B3 9
+#define SI_RAM_ADDR_LO 10
+
+#define SI_RAM_OSC1FREQ 26
+#define SI_RAM_OSC1AMP 27
+#define SI_RAM_OSC1PHAS 28
+#define SI_RAM_OSC2FREQ 29
+#define SI_RAM_OSC2AMP 30
+#define SI_RAM_OSC2PHAS 31
+
+#define SI_RAM_FSKAMP0 836
+#define SI_RAM_FSKAMP1 837
+#define SI_RAM_FSKFREQ0 834
+#define SI_RAM_FSKFREQ1 835
+#define SI_RAM_FSK01 838
+#define SI_RAM_FSK10 839
+
+#define SI_RAM_PRAM_ADDR 1358
+#define SI_RAM_PRAM_DATA 1359
+
+#define SI_RAM_PD_DCDC 1538
+
+/* define ram values */
+#define SI_RAM_VAL_REG (u32)(1 << 23)
+#define SI_RAM_VAL_OFF (u32)(1 << 20)
+
+/* define registers */
+#define SI_REG_MSTRSTAT 3
+#define SI_REG_RAMSTAT 4
+
+#define SI_REG_PCMMODE 11
+#define SI_REG_PCMTXLO 12
+#define SI_REG_PCMTXHI 13
+#define SI_REG_PCMRXLO 14
+#define SI_REG_PCMRXHI 15
+
+#define SI_REG_IRQ0 17
+#define SI_REG_IRQ1 18
+#define SI_REG_IRQ2 19
+#define SI_REG_IRQ3 20
+#define SI_REG_IRQ4 21
+
+#define SI_REG_IRQEN1 22
+#define SI_REG_IRQEN2 23
+#define SI_REG_IRQEN3 24
+#define SI_REG_IRQEN4 25
+
+#define SI_REG_CALR3 29
+
+#define SI_REG_LINEFEED 30
+
+#define SI_REG_LCRRTP 34
+
+#define SI_REG_DIGCON 44
+#define SI_REG_OMODE 48
+#define SI_REG_OCON 49
+#define SI_REG_TONEN 62
+
+#define SI_REG_O1TALO 50
+#define SI_REG_O1TAHI 51
+#define SI_REG_O1TILO 52
+#define SI_REG_O1TIHI 53
+#define SI_REG_O2TALO 54
+#define SI_REG_O2TAHI 55
+#define SI_REG_O2TILO 56
+#define SI_REG_O2TIHI 57
+
+#define SI_REG_FSKDAT 58
+#define SI_REG_FSKDEPTH 59
+#define SI_REG_TONDTMF 60
+
+#define SI_REG_JMPEN 81
+#define SI_REG_JMP0LO 82
+#define SI_REG_JMP7HI 97
+#define SI_REG_UAM 126
+
+
+/* define register values */
+#define SI_VAL_FSKBUF_AVAIL_IA ((u8)0x40)
+#define SI_VAL_RXMDM_IA ((u8)0x80)
+#define SI_VAL_TXMDM_IA ((u8)0x40)
+#define SI_VAL_DTMF_IA ((u8)0x10)
+#define SI_VAL_LCR_IA ((u8)0x2)
+#define SI_VAL_P_TERM_IA ((u8)0x2)
+#define SI_VAL_HYB_DIS ((u8)0x10)
+#define SI_VAL_CAL_EN ((u8)0x80)
+#define SI_VAL_ROUTING_1_3 ((u8)0x3)
+#define SI_VAL_ROUTING_1_2 ((u8)0x2)
+#define SI_VAL_ROUTING_2_3 ((u8)0x30)
+#define SI_VAL_OSC1_TA_EN ((u8)0x4)
+#define SI_VAL_OSC1_TI_EN ((u8)0x2)
+#define SI_VAL_OSC1_EN ((u8)0x1)
+#define SI_VAL_OSC2_TA_EN ((u8)0x40)
+#define SI_VAL_OSC2_TI_EN ((u8)0x20)
+#define SI_VAL_OSC2_EN ((u8)0x10)
+
+#define SI_VAL_VALID ((u8)0x20)
+
+#define SI_VAL_ZERO_EN_1 ((u8)0x4)
+#define SI_VAL_ZERO_EN_2 ((u8)0x40)
+
+#define SI_VAL_OSC1_FSK ((u8)0x8)
+
+#define SI_VAL_FSK_FLUSH ((u8)0x8)
+#define SI_VAL_FSKBUF_DEPTH_7 ((u8)0x7)
+
+#define SI_VAL_PCM_EN ((u8)0x10)
+#define SI_VAL_PCM_FMT_0 ((u8)0x0)
+#define SI_VAL_PCM_FMT_1 ((u8)0x1)
+
+#define SI_VAL_PCM_ALAW_0 ((u8)0x0)
+#define SI_VAL_PCM_ALAW_1 ((u8)0x4)
+#define SI_VAL_PCM_ALAW_2 ((u8)0x8)
+#define SI_VAL_PCM_ALAW_3 ((u8)0xC)
+
+#define SI_VAL_LCR ((u8)0x2)
+
+#define SI_VAL_EN_SYNC_1 ((u8)0x8)
+#define SI_VAL_EN_SYNC_2 ((u8)0x80)
+/*
+ * si3226x hardware line states
+ */
+enum fxs_states {
+        SI_FXS_OPEN,
+        SI_FXS_FORWARD_ACTIVE,
+        SI_FXS_FORWARD_OHT,
+        SI_FXS_TIP_OPEN,
+        SI_FXS_RINGING,
+        SI_FXS_REVERSE_ACTIVE,
+        SI_FXS_REVERSE_OHT,
+        SI_FXS_RING_OPEN,
+};
+
+/*
+ * registers content for generate one DTMF digit
+ */
+struct si3226x_dtmf_digit {
+        u32 osc1amp;
+        u32 osc2amp;
+        u32 osc1freq;
+        u32 osc2freq;
+};
+
+/*
+ * ATS answer tone register values
+ */
+struct si3226x_timer_regs {
+        u32 osc_amp;
+        u32 osc_freq;
+        u8 o_talo;
+        u8 o_tahi;
+        u8 o_tilo;
+        u8 o_tihi;
+        u8 o_con;
+};
+
+/*
+ * ATS answer tone types
+ */
+enum tone_types {
+        SI_TONE_INVITATION,
+        SI_TONE_BUSY,
+        SI_TONE_WAIT,
+        SI_TONE_NONE,
+};
+
+
+/*
+ * si3226x hardware errors
+ */
+enum si3226x_errors {
+        SI_ERR_TERMAL_SHOCK,
+};
+
+//extern struct si3226x_dtmf_digit slic_dtmf_table[];
+
+int init_slic(struct si3226x_slic *slic);
+int deinit_slic(struct si3226x_slic *slic);
+int slic_setup_audio(struct si3226x_line *line);
+int slic_set_line_state(struct si3226x_line *line, int state);
+int slic_line_call(struct si3226x_line *line, u8 *callerid_buf, int size);
+int slic_send_dtmf_digit(struct si3226x_line *line, char ch);
+int slic_calibrate(struct si3226x_line *line);
+int slic_enable_echo(struct si3226x_line *line);
+int slic_disable_echo(struct si3226x_line *line);
+irqreturn_t slic_irq(s32 irq, void *context_data);
+int slic_unlock_channel(struct si3226x_line *line);
+int slic_lock_channel(struct si3226x_line *line);
+int slic_write_ram(struct si3226x_line *line, u16 addr, u32 value);
+int slic_write_reg(struct si3226x_line *line, u8 addr, u8 data);
+void slic_irq_callback(struct work_struct *work);
+void do_line_call(struct work_struct *work);
+
+
+/*
+ * Sleep current process during timeout "msec"
+ */
+#define si_msleep(msec) \
+{	\
+	unsigned long timeout = jiffies + MSEC(msec);	\
+	while(!time_after(jiffies, timeout))	\
+		schedule();	\
+}
+
+
+
+#ifdef CONFIG_SI3226X_POLLING
+void slic_delayed_work(struct delayed_work *work);
+#endif
+
+#endif /* SI3226X_HW_H_ */
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_patch_C_FB_2011MAY19.c linux-3.8-rc5/drivers/staging/si3226x/si3226x_patch_C_FB_2011MAY19.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_patch_C_FB_2011MAY19.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_patch_C_FB_2011MAY19.c	2013-01-24 19:18:30.000000000 +0300
@@ -0,0 +1,176 @@
+/*
+** Generated from si3226x_patch_C_FB_2011MAY19.dsp_prom
+** Based on design file si32260C_firmware_1_0_0_4_20110517
+** on 05-19-2011 at 14:58:49
+** Patch ID = 0x1004C000
+*/
+
+const u32 si3226x_patch_data[] =
+{
+	141541L,
+	540867L,
+	141541L,
+	543427L,
+	141541L,
+	553155L,
+	141541L,
+	577731L,
+	141541L,
+	579779L,
+	141541L,
+	581315L,
+	141541L,
+	581827L,
+	141541L,
+	582339L,
+	141541L,
+	582851L,
+	141541L,
+	583363L,
+	141541L,
+	583875L,
+	141541L,
+	584387L,
+	141541L,
+	584899L,
+	141541L,
+	585411L,
+	141541L,
+	585923L,
+	141541L,
+	586435L,
+	736L,
+	491712L,
+	452200L,
+	141541L,
+	491200L,
+	5733L,
+	524290L,
+	142565L,
+	550083L,
+	3685L,
+	519266L,
+	5220L,
+	144098L,
+	550083L,
+	3685L,
+	524291L,
+	141541L,
+	551619L,
+	5221L,
+	3682L,
+	524292L,
+	5L,
+	141541L,
+	135362L,
+	98021L,
+	727745L,
+	474213L,
+	17637L,
+	557251L,
+	101093L,
+	557251L,
+	473701L,
+	515653L,
+	843365L,
+	188002L,
+	843355L,
+	145125L,
+	560835L,
+	524290L,
+	660069L,
+	518053L,
+	517224L,
+	518244L,
+	142565L,
+	564419L,
+	524288L,
+	521733L,
+	843365L,
+	188002L,
+	524315L,
+	145125L,
+	568003L,
+	843365L,
+	522850L,
+	523387L,
+	147685L,
+	573123L,
+	522363L,
+	145125L,
+	575171L,
+	521826L,
+	141541L,
+	575683L,
+	518757L,
+	521826L,
+	141541L,
+	575683L,
+	521824L,
+	522245L,
+	522338L,
+	141541L,
+	716481L,
+	173669L,
+	523845L,
+	141541L,
+	730304L,
+	523877L,
+	141541L,
+	690368L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	524293L,
+	0L
+};
+
+const u16 si3226x_patch_entries[] =
+{
+	950,
+	4347,
+	3431,
+	1425,
+	1347,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0
+};
+
+const u16 si3226x_patch_support_addr[] =
+{
+	925,
+	926,
+	1014,
+	1020,
+	1021,
+	1022,
+	0
+};
+
+const u32 si3226x_patch_support_data[] =
+{
+	0xA00000L,
+	0x1F00000L,
+	0x2D8000L,
+	0x0L,
+	0x1A9FBDAL,
+	0x1C28F4EL,
+	0x0L
+};
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_setup.c linux-3.8-rc5/drivers/staging/si3226x/si3226x_setup.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/si3226x_setup.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/si3226x_setup.c	2013-01-28 17:20:50.000000000 +0300
@@ -0,0 +1 @@
+int slic_load_settings(struct si3226x_slic *slic){        int rc = 0;        struct si3226x_line *lines = slic->lines;        unsigned long timeout;        rc = slic_unlock_channel(lines + 0);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 47, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 80, 0x2F);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 764, 0x0020C480);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 768, 0x051EB82A);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 767, 0x03D70A20);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 915, 0x0FFF0000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 916, 0x01999A00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 919, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 920, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 970, 0x00800000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1004, 0x00F18900);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1005, 0x00809D80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1006, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1007, 0x01C00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1540, 0x00400000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1541, 0x00400000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1542, 0x00200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1545, 0x00500000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1546, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1547, 0x00500000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1553, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1554, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1558, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1560, 0x00200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1585, 0x00300000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1586, 0x00300000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1587, 0x00100000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1588, 0x00FFC000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1589, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1590, 0x0FDA4000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 759, 0x07FEB800);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 756, 0x005B05B2);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 73, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 967, 0x03A2E8BA);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1018, 0x03000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1017, 0x05000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1013, 0x01000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1012, 0x03700000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1011, 0x04B80200);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1010, 0x00823000);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 68, 0x60);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 98, 0x80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 533, 0x71EB851);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 626, 0x723F235);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 627, 0x57A9804);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 918, 0x0036000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1616, 0x1100000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 973, 0xFFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 975, 0xE49BA5);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 516, 0x10038D);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 513, 0x4EDDB9);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 514, 0x0806D6);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1641, 0x200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1643, 0x000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1565, 0xC00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 750, 0x206280);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 971, 0x1F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 972, 0x51EB80);        if (rc)		return rc;        rc = slic_lock_channel(lines + 0);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 47, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 80, 0x2F);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 764, 0x0020C480);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 768, 0x051EB82A);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 767, 0x03D70A20);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 915, 0x0FFF0000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 916, 0x01999A00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 919, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 920, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 970, 0x00800000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1004, 0x00F18900);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1005, 0x00809D80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1006, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1007, 0x01C00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1540, 0x00400000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1541, 0x00400000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1542, 0x00200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1545, 0x00500000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1546, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1547, 0x00500000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1553, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1554, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1558, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1560, 0x00200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1585, 0x00300000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1586, 0x00300000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1587, 0x00100000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1588, 0x00FFC000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1589, 0x00F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1590, 0x0FDA4000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 759, 0x07FEB800);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 756, 0x005B05B2);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 73, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 967, 0x03A2E8BA);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1018, 0x03000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1017, 0x05000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1013, 0x01000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1012, 0x03700000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1011, 0x04B80200);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1010, 0x00823000);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 68, 0x60);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 98, 0x80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 533, 0x71EB851);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 626, 0x723F235);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 627, 0x57A9804);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 918, 0x0036000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1616, 0x1100000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 973, 0xFFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 975, 0xE49BA5);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 516, 0x10038D);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 513, 0x4EDDB9);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 514, 0x0806D6);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1641, 0x200000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1643, 0x000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1565, 0xC00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 750, 0x206280);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 971, 0x1F00000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 972, 0x51EB80);        if (rc)		return rc;        rc = slic_lock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 27, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 28, 0x01);        if (rc)		return rc;        rc = slic_calibrate(lines + 0);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 27, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 28, 0x01);        if (rc)		return rc;        rc = slic_calibrate(lines + 1);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 0);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1548, 0x00800000);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 30, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 47, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1538, 0x700000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1555, 0x100000);        if (rc)		return rc;        timeout = jiffies + MSEC(100);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_write_ram(lines + 0, 1538, 0x600000);        if (rc)		return rc;        timeout = jiffies + MSEC(500);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_write_ram(lines + 0, 1551, 0x000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1538, 0x400000);        if (rc)		return rc;        timeout = jiffies + MSEC(500);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_lock_channel(lines + 0);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1548, 0x00800000);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 30, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 47, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1538, 0x700000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1555, 0x100000);        if (rc)		return rc;        timeout = jiffies + MSEC(100);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_write_ram(lines + 1, 1538, 0x600000);        if (rc)		return rc;        timeout = jiffies + MSEC(500);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_write_ram(lines + 1, 1551, 0x000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1538, 0x400000);        if (rc)		return rc;        timeout = jiffies + MSEC(500);        while(!time_after(jiffies, timeout))        schedule();        rc = slic_lock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 27, 0xC0);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 28, 0x18);        if (rc)		return rc;        rc = slic_calibrate(lines + 0);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 27, 0xC0);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 28, 0x18);        if (rc)		return rc;        rc = slic_calibrate(lines + 1);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 0);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 47, 0x10);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 80, 0x3F);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 46, 0x04);        if (rc)		return rc;        rc = slic_lock_channel(lines + 0);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 47, 0x10);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 80, 0x3F);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 46, 0x04);        if (rc)		return rc;        rc = slic_lock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 540, 0x07F97D80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 541, 0x0006CC00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 542, 0x1FFC1480);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 543, 0x1FFC7B80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 546, 0x07F36B80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 547, 0x000A8E00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 548, 0x1FF90F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 549, 0x1FFAE500);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 563, 0x001AF400);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 564, 0x1FC86A80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 565, 0x01E9AE00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 566, 0x00652F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 567, 0x01F4AF00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 568, 0x1F57E000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 569, 0x00485E00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 570, 0x1FF3A680);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 571, 0x1FF83700);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 572, 0x00011D00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 573, 0x01706980);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 574, 0x066A8480);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 653, 0x00920F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 654, 0x1EE31980);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 655, 0x008ADF00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 656, 0x0F92E500);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 657, 0x186CE880);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 45, 0x53);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 544, 0x085C6880);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 906, 0x013E3100);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 545, 0x013E3100);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 658, 0x07AF6F80);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 659, 0x18509100);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 660, 0x075EDF00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 27, 0x40);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 28, 0x00);        if (rc)		return rc;        rc = slic_calibrate(lines + 0);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 540, 0x07F97D80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 541, 0x0006CC00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 542, 0x1FFC1480);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 543, 0x1FFC7B80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 546, 0x07F36B80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 547, 0x000A8E00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 548, 0x1FF90F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 549, 0x1FFAE500);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 563, 0x001AF400);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 564, 0x1FC86A80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 565, 0x01E9AE00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 566, 0x00652F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 567, 0x01F4AF00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 568, 0x1F57E000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 569, 0x00485E00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 570, 0x1FF3A680);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 571, 0x1FF83700);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 572, 0x00011D00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 573, 0x01706980);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 574, 0x066A8480);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 653, 0x00920F00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 654, 0x1EE31980);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 655, 0x008ADF00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 656, 0x0F92E500);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 657, 0x186CE880);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 45, 0x53);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 544, 0x085C6880);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 906, 0x013E3100);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 545, 0x013E3100);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 658, 0x07AF6F80);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 659, 0x18509100);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 660, 0x075EDF00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 26, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 27, 0x40);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 28, 0x00);        if (rc)		return rc;        rc = slic_calibrate(lines + 1);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 755, 0x00050000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 844, 0x07EFE000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 845, 0x001B9F2E);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 846, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 843, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 637, 0x15E5200E);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 860, 0x00D16348);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 848, 0x0068E9B4);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 847, 0x0FFFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 850, 0x00006000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 849, 0x00006000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 753, 0x00C49BA0);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 896, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 768, 0x051EB82A);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 39, 0x80);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 40, 0x3E);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 41, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 42, 0x7D);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 920, 0x01893740);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 38, 0x98);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 66, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 749, 0x02AC55FE);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 482, 0x02AC55FE);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 483, 0x003126E8);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 972, 0x00FFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 974, 0x0083126A);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 975, 0x009374B8);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 0);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 1560, 0x00200000);        if (rc)		return rc;        rc = slic_lock_channel(lines + 0);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 755, 0x00050000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 844, 0x07EFE000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 845, 0x001B9F2E);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 846, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 843, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 637, 0x15E5200E);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 860, 0x00D16348);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 848, 0x0068E9B4);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 847, 0x0FFFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 850, 0x00006000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 849, 0x00006000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 753, 0x00C49BA0);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 896, 0x00000000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 768, 0x051EB82A);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 39, 0x80);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 40, 0x3E);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 41, 0x00);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 42, 0x7D);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 920, 0x01893740);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 38, 0x98);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 66, 0x00);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 749, 0x02AC55FE);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 482, 0x02AC55FE);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 483, 0x003126E8);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 972, 0x00FFFFFF);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 974, 0x0083126A);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 975, 0x009374B8);        if (rc)		return rc;        rc = slic_unlock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 1560, 0x00200000);        if (rc)		return rc;        rc = slic_lock_channel(lines + 1);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 634, 0x1C8A024C);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 635, 0x1F909679);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 636, 0x0040A0E0);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 638, 0x1D5B21A9);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 639, 0x1DD87A3E);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 640, 0x05A38633);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 641, 0x050D2839);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 642, 0x03FE7F0F);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 643, 0x00B4F3C3);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 644, 0x005D0FA6);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 645, 0x002D8D96);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 853, 0x005B0AFB);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 852, 0x006D4060);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 701, 0x00008000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 858, 0x0048D595);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 859, 0x003FBAE2);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 702, 0x00008000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 854, 0x000F0000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 855, 0x00080000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 856, 0x00140000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 857, 0x00140000);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 748, 0x01BA5E35);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 752, 0x0051EB85);        if (rc)		return rc;        rc = slic_write_ram(lines + 0, 751, 0x00418937);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 634, 0x1C8A024C);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 635, 0x1F909679);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 636, 0x0040A0E0);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 638, 0x1D5B21A9);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 639, 0x1DD87A3E);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 640, 0x05A38633);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 641, 0x050D2839);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 642, 0x03FE7F0F);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 643, 0x00B4F3C3);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 644, 0x005D0FA6);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 645, 0x002D8D96);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 853, 0x005B0AFB);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 852, 0x006D4060);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 701, 0x00008000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 858, 0x0048D595);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 859, 0x003FBAE2);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 702, 0x00008000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 854, 0x000F0000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 855, 0x00080000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 856, 0x00140000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 857, 0x00140000);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 748, 0x01BA5E35);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 752, 0x0051EB85);        if (rc)		return rc;        rc = slic_write_ram(lines + 1, 751, 0x00418937);        if (rc)		return rc;        rc = slic_write_reg(lines + 0, 30, 0x1);        if (rc)		return rc;        rc = slic_write_reg(lines + 1, 30, 0x1);        if (rc)		return rc;        return rc;}
\ В конце файла нет новой строки
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/si3226x/slic_dtmf_table.c linux-3.8-rc5/drivers/staging/si3226x/slic_dtmf_table.c
--- linux-3.8-rc5-vanilla/drivers/staging/si3226x/slic_dtmf_table.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/si3226x/slic_dtmf_table.c	2012-10-26 13:51:32.000000000 +0300
@@ -0,0 +1,127 @@
+
+/*
+ * dtmf table for convert DTMF - ASCII
+ */
+char dtmf_codes[] = {"D1234567890*#ABC"};
+
+
+/*
+ * si3226x DTMF tone registers configure per digit
+ */
+struct si3226x_dtmf_digit slic_dtmf_table[] =
+{
+	
+    {  // D
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0xb2c000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x5e9c000,
+    },
+	
+    {  // 1
+        .osc1amp = 0xed2000,
+        .osc2amp = 0x818000,
+        .osc1freq = 0x4a82000,
+        .osc2freq = 0x6d4c000,
+    },
+	
+    {  // 2
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0x818000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x6d4c000,
+    },
+	
+    {  // 3
+        .osc1amp = 0x12e4000,
+        .osc2amp = 0x818000,
+        .osc1freq = 0x331e000,
+        .osc2freq = 0x6d4c000,
+    },
+	
+    {  // 4
+        .osc1amp = 0xed2000,
+        .osc2amp = 0x900000,
+        .osc1freq = 0x4a82000,
+        .osc2freq = 0x694e000,
+    },
+	
+    {  // 5
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0x900000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x694e000,
+    },
+	
+    {  // 6
+        .osc1amp = 0x12e4000,
+        .osc2amp = 0x900000,
+        .osc1freq = 0x331e000,
+        .osc2freq = 0x694e000,
+    },
+	
+    {  // 7
+        .osc1amp = 0xed2000,
+        .osc2amp = 0xa06000,
+        .osc1freq = 0x4a82000,
+        .osc2freq = 0x6466000,
+    },
+	
+    {  // 8
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0xa06000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x6466000,
+    },
+	
+    {  // 9
+        .osc1amp = 0x12e4000,
+        .osc2amp = 0xa06000,
+        .osc1freq = 0x331e000,
+        .osc2freq = 0x6466000,
+    },
+	
+    {  // 0
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0xb2c000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x5e9c000,
+    },
+	
+    {  // *
+        .osc1amp = 0xed2000,
+        .osc2amp = 0xb2c000,
+        .osc1freq = 0x4a82000,
+        .osc2freq = 0x5e9c000,
+    },
+	
+    {  // #
+        .osc1amp = 0x10b0000,
+        .osc2amp = 0xb2c000,
+        .osc1freq = 0x3fc6000,
+        .osc2freq = 0x5e9c000,
+    },
+	
+    {  // A
+        .osc1amp = 0x1586000,
+        .osc2amp = 0x818000,
+        .osc1freq = 0x2464000,
+        .osc2freq = 0x6d4c000,
+    },
+	
+    {  // B
+        .osc1amp = 0x1586000,
+        .osc2amp = 0x900000,
+        .osc1freq = 0x2464000,
+        .osc2freq = 0x694e000,
+    },
+	
+    {  // C
+        .osc1amp = 0x1586000,
+        .osc2amp = 0xa06000,
+        .osc1freq = 0x2464000,
+        .osc2freq = 0x6466000,
+    },
+	
+};
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/tdm/Kconfig linux-3.8-rc5/drivers/staging/tdm/Kconfig
--- linux-3.8-rc5-vanilla/drivers/staging/tdm/Kconfig	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/tdm/Kconfig	2012-10-31 19:34:56.000000000 +0300
@@ -0,0 +1,38 @@
+#
+# TDM driver configuration
+#
+menuconfig TDM
+	bool "TDM support"
+	depends on HAS_IOMEM
+	help
+	  Time-division multiplexing (TDM) is a type of digital (or rarely analog)
+	  multiplexing in which two or more bit streams or signals are
+	  transferred apparently simultaneously as sub-channels in one
+	  communication channel, but are physically taking turns on the
+	  channel. The time domain is divided into several recurrent
+	  timeslots of fixed length, one for each sub-channel. A sample
+	  byte or data block of sub-channel 1 is transmitted during timeslot 1,
+	  sub-channel 2 during timeslot 2, etc. One TDM frame consists of one
+	  timeslot per sub-channel plus a synchronization channel and
+	  sometimes error correction channel before the synchronization.
+
+if TDM
+
+config TDM_DEBUG
+	bool "Debug support for TDM drivers"
+	depends on DEBUG_KERNEL
+	help
+	  Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
+	  sysfs, and debugfs support in TDM controller and protocol drivers.
+
+
+comment "TDM controllers"
+
+config TDM_KIRKWOOD
+#  TODO: add depend on kirkwood architecture
+#	depends on ARCH_KIRKWOOD
+	tristate "Marvel Kirkwood TDM Controller"
+	help
+	  This selects a driver for the Marvel Kirkwood TDM Controller.
+		  
+endif # TDM
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/tdm/kirkwood_tdm.c linux-3.8-rc5/drivers/staging/tdm/kirkwood_tdm.c
--- linux-3.8-rc5-vanilla/drivers/staging/tdm/kirkwood_tdm.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/tdm/kirkwood_tdm.c	2013-01-30 15:00:14.669735000 +0300
@@ -0,0 +1,929 @@
+/**********************************************************************
+ * Author: Michail Kurachkin
+ *
+ * Contact: michail.kurachkin@promwad.com
+ *
+ * Copyright (c) 2013 Promwad Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+**********************************************************************/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tdm/tdm.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/inet_lro.h>
+#include <linux/slab.h>
+#include <asm/system.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/mbus.h>
+#include <linux/miscdevice.h>
+
+
+#include <linux/platform_device.h>
+#include "kirkwood_tdm.h"
+
+
+/**
+ * Init hardware tdm controller
+ * @param tdm - tdm_controller descriptor
+ * @return
+ */
+static int kirkwood_tdm_hw_init(struct tdm_controller *tdm)
+{
+	struct kirkwood_tdm *onchip_tdm =
+	    (struct kirkwood_tdm *)dev_get_drvdata(&tdm->dev);
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+	struct tdm_controller_hw_settings *hw = tdm->settings;
+	u32 control_val = 0;
+	u32 pclk_freq;
+	unsigned long flags;
+	spinlock_t lock;
+	spin_lock_init(&lock);
+
+
+	// Errata GL-CODEC-10: resolve problem witch FS
+	spin_lock_irqsave(&lock, flags);
+	writel(0x03ffff00, &regs->pcm_ctrl_reg + 0x1000);
+	writel(5, &regs->pcm_ctrl_reg + 0x101C);
+	spin_unlock_irqrestore(&lock, flags);
+
+
+	writel(0, &regs->int_reset_sel);
+	writel(0x3ffff, &regs->int_event_mask);
+	writel(0, &regs->int_status_mask);
+	writel(0, &regs->int_status);
+
+	writeb(0x41, &regs->pcm_ctrl_reg + 0xC41);
+	writeb(0x10, &regs->pcm_ctrl_reg + 0xC40);
+
+	// Configuring PCLK and FS frequency
+	pclk_freq = hw->count_time_slots * 8 * hw->fs_freq;
+	switch(pclk_freq) {
+	case 256:
+		writel(0x1, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x4, &regs->num_time_slot);
+		break;
+
+	case 512:
+		writel(0x2, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x8, &regs->num_time_slot);
+		break;
+
+	case 1024:
+		writel(0x4, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x10, &regs->num_time_slot);
+		break;
+
+	case 2048:
+		writel(0x8, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x20, &regs->num_time_slot);
+		break;
+
+	case 4096:
+		writel(0x10, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x40, &regs->num_time_slot);
+		break;
+
+	case 8192:
+		writel(0x20, &regs->tdm_pcm_clock_div);
+		writel(0x0, &regs->dummy_rx_write);
+		writel(0x80, &regs->num_time_slot);
+		break;
+
+	default:
+		dev_err(&tdm->dev, "Incorrect count of time slots parameter\n");
+		return -EINVAL;
+	}
+
+	control_val = readl(&regs->pcm_ctrl_reg);
+
+	if(hw->clock_direction == TDM_CLOCK_OUTPUT)
+		control_val &= (u32)~(1 << 0);
+	else
+		control_val |= (1 << 0);
+
+
+	if(hw->fs_clock_direction == TDM_CLOCK_OUTPUT)
+		control_val &= (u32) ~(1 << 1);
+	else
+		control_val |= (1 << 1);
+
+
+	if(hw->fs_polarity == TDM_POLAR_POSITIV)
+		control_val &= (u32) ~(1 << 4);
+	else
+		control_val |= (1 << 4);
+
+
+	if(hw->data_polarity == TDM_POLAR_POSITIV)
+		control_val &= (u32) ~(1 << 2);
+	else
+		control_val |= (1 << 2);
+
+
+	if(hw->channel_size == 1)
+		control_val &= (u32) ~(1 << 6);
+	else
+		control_val |= (1 << 6);
+
+	writel(control_val, &regs->pcm_ctrl_reg);
+	writel(0, &regs->chan_time_slot_ctrl);
+	writel(0, &regs->chan0_enable_disable);
+	writel(0, &regs->chan0_enable_disable + 4);
+
+	return 0;
+}
+
+/**
+ * deInitialization hardware tdm controller
+ * @param tdm - tdm_controller descriptor
+ */
+static void kirkwood_tdm_hw_deinit(struct tdm_controller *tdm)
+{
+	struct kirkwood_tdm *onchip_tdm =
+	    (struct kirkwood_tdm *)dev_get_drvdata(&tdm->dev);
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+
+	writel(0, &regs->pcm_ctrl_reg);
+	writel(0, &regs->chan_time_slot_ctrl);
+}
+
+/**
+ * Setup hardware voice channel
+ * @param ch - voice hardware channel
+ * @return 0 - OK
+ */
+int kirkwood_setup_voice_channel(struct tdm_voice_channel* ch)
+{
+	struct tdm_device *tdm_dev = to_tdm_device(ch->dev);
+	struct tdm_controller *tdm = tdm_dev->controller;
+	struct tdm_controller_hw_settings *hw = tdm->settings;
+	struct kirkwood_tdm *onchip_tdm =
+	    (struct kirkwood_tdm *)dev_get_drvdata(&tdm->dev);
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+	unsigned long timeout;
+	u32 state;
+	int i;
+
+	u8 voice_num = ch->channel_num;
+
+	/* calculate buffer size */
+	ch->buffer_len = tdm_dev->buffer_sample_count * hw->channel_size;
+
+	/* Request timeslot for voice channel */
+	writeb(ch->tdm_channel, (u8*)&regs->chan_time_slot_ctrl + 2 * voice_num);
+	writeb(ch->tdm_channel, (u8*)&regs->chan_time_slot_ctrl + 1 + 2 * voice_num);
+
+	/* FIXME: move coherent_dma_mask to board specific */
+	tdm_dev->dev.coherent_dma_mask = 0xffffffff;
+
+	/* Allocate rx and tx buffers */
+	for(i = 0; i < COUNT_DMA_BUFFERS_PER_CHANNEL; i++) {
+		/* allocate memory for DMA receiver */
+		onchip_ch->rx_buf[i] =
+		        dma_alloc_coherent(&tdm_dev->dev,
+		        ch->buffer_len, onchip_ch->rx_buff_phy + i, GFP_DMA);
+
+		if (onchip_ch->rx_buf == NULL) {
+			dev_err(ch->dev, "Can't allocate memory for TDM receiver DMA buffer\n");
+			return -ENOMEM;
+		}
+		memset(onchip_ch->rx_buf[i], 0, ch->buffer_len);
+
+
+		/* allocate memory for DMA transmitter */
+		onchip_ch->tx_buf[i] =
+		        dma_alloc_coherent(&tdm_dev->dev,
+		        ch->buffer_len, onchip_ch->tx_buff_phy + i, GFP_DMA);
+
+		if (onchip_ch->tx_buf == NULL) {
+			dev_err(ch->dev, "Can't allocate memory for TDM transmitter DMA buffer\n");
+			return -ENOMEM;
+		}
+		memset(onchip_ch->tx_buf[i], 0, ch->buffer_len);
+	}
+
+	atomic_set(&onchip_ch->write_rx_buf_num, 0);
+	atomic_set(&onchip_ch->write_tx_buf_num, 0);
+	atomic_set(&onchip_ch->read_rx_buf_num, 0);
+	atomic_set(&onchip_ch->read_tx_buf_num, 0);
+
+	/* Set length for DMA */
+	writel(((tdm_dev->buffer_sample_count - 32) << 8) | tdm_dev->buffer_sample_count,
+	       &regs->chan0_total_sample + voice_num);
+
+	/* Waiting for program transmit DMA */
+	timeout = jiffies + HZ;
+	do {
+		state = readl(&regs->chan0_buff_ownership + 4 * voice_num) & 0x100;
+		if(time_after(jiffies, timeout)) {
+			dev_err(ch->dev, "Can`t program DMA tx buffer\n");
+			return -EBUSY;
+		}
+	} while(state);
+
+	/* Set DMA buffers fo transmitter */
+	writel((u32)onchip_ch->tx_buff_phy[0],
+	    &regs->chan0_transmit_start_addr + 4 * voice_num);
+	writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num) + 1);
+
+	/* Waiting for program received DMA */
+	timeout = jiffies + HZ;
+	do {
+		state = readl(&regs->chan0_buff_ownership + 4 * voice_num) & 1;
+		if(time_after(jiffies, timeout)) {
+			dev_err(ch->dev, "Can`t program DMA rx buffer\n");
+			return -EBUSY;
+		}
+	} while(state);
+
+	/* Set DMA buffers for receiver */
+	writel((u32)onchip_ch->rx_buff_phy[0],
+	    &regs->chan0_receive_start_addr + 4 * voice_num);
+	writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num) );
+
+	return 0;
+}
+
+
+/**
+ * Run tdm transmitter and receiver
+ * @param tdm_dev - tdm device
+ * @return 0 - ok
+ */
+int kirkwood_tdm_run_audio(struct tdm_device *tdm_dev)
+{
+	struct tdm_controller *tdm = tdm_dev->controller;
+	struct kirkwood_tdm *onchip_tdm =
+	        (struct kirkwood_tdm *)dev_get_drvdata(&tdm->dev);
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+	struct tdm_voice_channel *ch = tdm_dev->ch;
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+
+	memset(onchip_ch->tx_buf[0], 0, ch->buffer_len);
+	memset(onchip_ch->tx_buf[1], 0, ch->buffer_len);
+
+	writeb(0x1, (u8 *)(&regs->chan0_enable_disable + 4 * ch->channel_num) + 1);
+	writeb(0x1, (u8 *)(&regs->chan0_enable_disable + 4 * ch->channel_num) );
+
+	/* enable Tx interrupts */
+	writel(0, &regs->int_status);
+	writel((readl(&regs->int_status_mask) | TDM_INT_TX(ch->channel_num)),
+	        &regs->int_status_mask);
+
+	/* enable Rx interrupts */
+	writel(0, &regs->int_status);
+	writel((readl(&regs->int_status_mask) | TDM_INT_RX(ch->channel_num)),
+	        &regs->int_status_mask);
+
+	writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * ch->channel_num));
+	writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * ch->channel_num) + 1);
+
+	return 0;
+}
+
+
+/**
+ * Stop tdm transmitter and receiver
+ * @param tdm_dev - tdm device
+ * @return 0 - ok
+ */
+int kirkwood_tdm_stop_audio(struct tdm_device *tdm_dev)
+{
+	struct tdm_controller *tdm = tdm_dev->controller;
+	struct kirkwood_tdm *onchip_tdm =
+			(struct kirkwood_tdm *)dev_get_drvdata(&tdm->dev);
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+	struct tdm_voice_channel *ch = tdm_dev->ch;
+
+	/* disable Tx interrupts */
+	writel((readl(&regs->int_status_mask) & (~TDM_INT_TX(ch->channel_num))),
+	    &regs->int_status_mask);
+	writel(0, &regs->int_status);
+
+	/* disable Rx interrupts */
+	writel((readl(&regs->int_status_mask) & (~TDM_INT_RX(ch->channel_num))),
+	    &regs->int_status_mask);
+	writel(0, &regs->int_status);
+
+	writeb(0x0, (u8 *)(&regs->chan0_enable_disable + 4 * ch->channel_num) + 1);
+	writeb(0x0, (u8 *)(&regs->chan0_enable_disable + 4 * ch->channel_num) );
+
+	return 0;
+}
+
+
+/**
+ * Get DMA tx buffers latency
+ * @param onchip_ch - kirkwood based voice channel
+ * @return latency in buffers
+ */
+int get_tx_latency(struct kirkwood_tdm_voice *onchip_ch)
+{
+	if (atomic_read(&onchip_ch->read_tx_buf_num) <= atomic_read(&onchip_ch->write_tx_buf_num))
+		return atomic_read(&onchip_ch->write_tx_buf_num) - atomic_read(&onchip_ch->read_tx_buf_num);
+	else
+		return COUNT_DMA_BUFFERS_PER_CHANNEL - atomic_read(&onchip_ch->read_tx_buf_num)
+		       + atomic_read(&onchip_ch->write_tx_buf_num);
+}
+
+
+/**
+ * Get DMA rx buffers latency
+ * @param onchip_ch - kirkwood based voice channel
+ * @return latency in buffers
+ */
+int get_rx_latency(struct kirkwood_tdm_voice *onchip_ch)
+{
+	if (atomic_read(&onchip_ch->read_rx_buf_num) <= atomic_read(&onchip_ch->write_rx_buf_num))
+		return atomic_read(&onchip_ch->write_rx_buf_num) - atomic_read(&onchip_ch->read_rx_buf_num);
+	else
+		return COUNT_DMA_BUFFERS_PER_CHANNEL - atomic_read(&onchip_ch->read_rx_buf_num)
+		       + atomic_read(&onchip_ch->write_rx_buf_num);
+}
+
+
+/**
+ * Check rx audio buffer for exist new data
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - not enought data, 1 - data exist
+ */
+int kirkwood_poll_rx(struct tdm_device *tdm_dev)
+{
+	struct tdm_voice_channel *ch = tdm_dev->ch;
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+
+	return get_rx_latency(onchip_ch) > 1;
+}
+
+
+/**
+ * Check tx audio buffer for free space
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - not enought free space, 1 - exist free space
+ */
+int kirkwood_poll_tx(struct tdm_device *tdm_dev)
+{
+	struct tdm_voice_channel *ch = tdm_dev->ch;
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+
+	return get_tx_latency(onchip_ch) > 1;
+}
+
+
+
+/**
+ * Get next dma buffer number
+ * @param num - current buffer number
+ * @return next buffer number
+ */
+static int inc_next_dma_buf_num(int num)
+{
+	num ++;
+	if (num >= COUNT_DMA_BUFFERS_PER_CHANNEL)
+		num = 0;
+
+	return num;
+}
+
+
+
+/**
+ * Send voice data block to tdm voice channel controller.
+ * @param ch - voice channel attendant to transmit data in TDM frame
+ * @param data - data to be transmit. Length of data must be equal to
+ * 		value returned by get_voice_block_size()
+ *
+ * Context: can sleep
+ * @return 0 on success; negative errno on failure
+ */
+static int kirkwood_send(struct tdm_voice_channel *ch, u8 *data)
+{
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+
+	wait_event_interruptible(ch->tx_queue,
+	                         get_tx_latency(onchip_ch) > 1);
+
+	memcpy(onchip_ch->tx_buf[atomic_read(&onchip_ch->read_tx_buf_num)], data,
+	       ch->buffer_len);
+
+	atomic_set(&onchip_ch->read_tx_buf_num,
+			inc_next_dma_buf_num(atomic_read(&onchip_ch->read_tx_buf_num)));
+
+	return 0;
+}
+
+
+/**
+ * Receive voice data block from TDM voice channel controller.
+ * @param ch - voice channel attendant to transmit data in TDM frame
+ * @param data - pointer to read data received by DMA.
+                 Length data for read equal to value returned by get_tdm_voice_block_size()
+ *
+ * Context: can sleep
+ * @return 0 on success; negative errno on failure
+ */
+static int kirkwood_recv(struct tdm_voice_channel *ch, u8 *data)
+{
+	struct kirkwood_tdm_voice *onchip_ch = ch->private_data;
+
+	wait_event_interruptible(ch->rx_queue,
+	                         get_rx_latency(onchip_ch) > 1);
+
+	memcpy(data, onchip_ch->rx_buf[atomic_read(&onchip_ch->read_rx_buf_num)],
+	       ch->buffer_len);
+
+	atomic_set(&onchip_ch->read_rx_buf_num,
+	           inc_next_dma_buf_num(atomic_read(&onchip_ch->read_rx_buf_num)));
+
+	return 0;
+}
+
+
+
+/**
+ * kirkwood_tdm_irq - IRQ handler for Kirkwood TDM
+ * @irq: IRQ number for this TDM controller
+ * @context_data: structure for TDM controller kirkwood_tdm
+ * Context: can not sleep
+ */
+static irqreturn_t kirkwood_tdm_irq(s32 irq, void *context_data)
+{
+	struct kirkwood_tdm *onchip_tdm = context_data;
+	struct kirkwood_tdm_regs *regs = onchip_tdm->regs;
+	struct tdm_controller *tdm = to_tdm_controller(onchip_tdm->controller_dev);
+	struct tdm_voice_channel *ch;
+	struct kirkwood_tdm_voice *onchip_ch;
+	struct tdm_device *tdm_dev;
+
+	irqreturn_t ret = IRQ_NONE;
+	u32 status;
+	u8 i;
+
+	int voice_num; /* current voice channel */
+	int next_buf_num; /* number of next buffer */
+	int mode; /* irq event mode: */
+	int overflow = 0;
+	int full = 0;
+
+	enum irq_event_mode {
+		IRQ_RECEIVE,
+		IRQ_TRANSMIT,
+	};
+
+	status = readl(&regs->int_status);
+
+	if ((status & 0xFF) == 0)
+		return ret;
+
+	/*  Check first 8 bit in status mask register for detect event type */
+	for(i = 0; i < 8; i++) {
+		if((status & (1 << i)) == 0)
+			continue;
+
+		writel(status & ~(1 << i), &regs->int_status);
+
+		switch(i) {
+		case 0:
+			mode = IRQ_RECEIVE;
+			voice_num = 0;
+			overflow = 1;
+			break;
+
+		case 1:
+			mode = IRQ_TRANSMIT;
+			voice_num = 0;
+			overflow = 1;
+			break;
+
+		case 2:
+			mode = IRQ_RECEIVE;
+			voice_num = 1;
+			overflow = 1;
+			break;
+
+		case 3:
+			mode = IRQ_TRANSMIT;
+			voice_num = 1;
+			overflow = 1;
+			break;
+
+		case 4:
+			mode = IRQ_RECEIVE;
+			voice_num = 0;
+			overflow = 0;
+			full = 0;
+			break;
+
+		case 5:
+			mode = IRQ_TRANSMIT;
+			voice_num = 0;
+			overflow = 0;
+			full = 0;
+			break;
+
+		case 6:
+			mode = IRQ_RECEIVE;
+			voice_num = 1;
+			overflow = 0;
+			full = 0;
+			break;
+
+		case 7:
+			mode = IRQ_TRANSMIT;
+			voice_num = 1;
+			overflow = 0;
+			full = 0;
+			break;
+		}
+
+		/* �urrent voice channel struct */
+		ch = get_voice_channel_by_num(tdm, voice_num);
+		onchip_ch = ch->private_data;
+
+		/* TDM device attached to current voice channel */
+		tdm_dev = to_tdm_device(ch->dev);
+
+		switch(mode) {
+		case IRQ_RECEIVE: {
+			/* get next buffer number, and move write/read pointer */
+			next_buf_num = inc_next_dma_buf_num(atomic_read(&onchip_ch->write_rx_buf_num));
+			atomic_set(&onchip_ch->write_rx_buf_num, next_buf_num);
+			if(next_buf_num == atomic_read(&onchip_ch->read_rx_buf_num))
+				atomic_set(&onchip_ch->read_rx_buf_num,
+				           inc_next_dma_buf_num(atomic_read(&onchip_ch->read_rx_buf_num)));
+
+			/* if receive overflow event */
+			if (overflow) {
+				/* set next buffer address */
+				writel((u32)onchip_ch->rx_buff_phy[next_buf_num],
+				       &regs->chan0_receive_start_addr + 4 * voice_num);
+				writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num));
+
+				/* enable receiver */
+				writeb(0x1, (u8 *)(&regs->chan0_enable_disable + 4 * voice_num));
+
+				ret = IRQ_HANDLED;
+				break;
+			}
+
+			/* waiting while dma providing access to buffer */
+			while(readl(&regs->chan0_buff_ownership + 4 * voice_num) & 1);
+
+			/* set next buffer address */
+			writel((u32)onchip_ch->rx_buff_phy[next_buf_num],
+			       &regs->chan0_receive_start_addr + 4 * voice_num);
+			writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num));
+
+			wake_up_interruptible(&ch->rx_queue);
+
+			ret = IRQ_HANDLED;
+		}
+		break;
+
+		case IRQ_TRANSMIT: {
+			/* get next buffer number, and move write/read pointer */
+			next_buf_num = inc_next_dma_buf_num(atomic_read(&onchip_ch->write_tx_buf_num));
+			atomic_set(&onchip_ch->write_tx_buf_num, next_buf_num);
+			if(next_buf_num == atomic_read(&onchip_ch->read_tx_buf_num))
+				atomic_set(&onchip_ch->read_tx_buf_num,
+				           inc_next_dma_buf_num(atomic_read(&onchip_ch->read_tx_buf_num)));
+
+			/* if transmit overflow event */
+			if (overflow) {
+				/* set next buffer address */
+				writel((u32)onchip_ch->tx_buff_phy[next_buf_num],
+				       &regs->chan0_transmit_start_addr + 4 * voice_num);
+				writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num) + 1);
+
+				/* enable transmitter */
+				writeb(0x1, (u8 *)(&regs->chan0_enable_disable + 4 * voice_num) + 1);
+
+				ret = IRQ_HANDLED;
+				break;
+			}
+
+			/* waiting while dma providing access to buffer */
+			while(readl(&regs->chan0_buff_ownership + 4 * voice_num) & 0x100);
+
+			/* set next buffer address */
+			writel((u32)onchip_ch->tx_buff_phy[next_buf_num],
+			       &regs->chan0_transmit_start_addr + 4 * voice_num);
+			writeb(0x1, (u8*)(&regs->chan0_buff_ownership + 4 * voice_num) + 1);
+
+			wake_up_interruptible(&ch->tx_queue);
+
+			ret = IRQ_HANDLED;
+		}
+		break;
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Configuring mbus windows for correct access to DMA memory
+ * @param base_regs - base address for tdm registers
+ * @param dram - dram settings
+ */
+static void kirkwood_tdm_mbus_windows(void *base_regs,
+                                      struct mbus_dram_target_info *dram)
+{
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		writel(0, (u8 *)base_regs + TDM_WINDOW_CTRL(i));
+		writel(0, (u8 *)base_regs + TDM_WINDOW_BASE(i));
+	}
+
+	for (i = 0; i < dram->num_cs; i++) {
+		struct mbus_dram_window *cs = dram->cs + i;
+
+		writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
+		       (dram->mbus_dram_target_id << 4) | 1,
+		       (u8 *)base_regs + TDM_WINDOW_CTRL(i));
+
+		writel(cs->base, (u8 *)base_regs + TDM_WINDOW_BASE(i));
+	}
+}
+
+
+
+static int __init kirkwood_tdm_probe(struct platform_device *pdev)
+{
+	int err = 0;
+	struct tdm_controller *tdm;
+	struct kirkwood_tdm *onchip_tdm = NULL;
+	struct resource *res;
+	struct tdm_controller_hw_settings *hw = pdev->dev.platform_data;
+	struct tdm_voice_channel *ch;
+	int i;
+
+	tdm = tdm_alloc_controller(&pdev->dev, sizeof(struct kirkwood_tdm));
+	if (tdm == NULL) {
+		dev_err(&pdev->dev, "Can`t alloc memory\n");
+		err = -ENOMEM;
+		goto out0;
+	}
+
+	platform_set_drvdata(pdev, tdm);
+	onchip_tdm = tdm_controller_get_devdata(tdm);
+
+	if (pdev->id != -1)
+		tdm->bus_num = pdev->id;
+
+	/* Check hardware settings */
+	if (hw->fs_freq != 8 && hw->fs_freq != 16) {
+		dev_err(&pdev->dev, "Fs frequency may be 8kHz o 16kHz. "
+		    "Frequency %d is not incorrect\n", hw->fs_freq);
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->count_time_slots > 64) {
+		dev_err(&pdev->dev, "Incorrect count time slots. "
+		    "No more than 64 timeslots per one FS. "
+		    "Current set %d\n", hw->count_time_slots);
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->channel_size > 2 || hw->channel_size == 0) {
+		dev_err(&pdev->dev, "Incorrect count time slots. "
+		    "No more than 64 timeslots per one FS. "
+		    "Current set %d\n", hw->count_time_slots);
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->clock_direction != TDM_CLOCK_INPUT &&
+	    hw->clock_direction != TDM_CLOCK_OUTPUT) {
+		dev_err(&pdev->dev, "Incorrect PCLK clock direction value\n");
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->fs_clock_direction != TDM_CLOCK_INPUT &&
+	    hw->fs_clock_direction != TDM_CLOCK_OUTPUT) {
+		dev_err(&pdev->dev, "Incorrect FS clock direction value\n");
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->fs_polarity != TDM_POLAR_NEGATIVE &&
+	    hw->fs_polarity != TDM_POLAR_POSITIV) {
+		dev_err(&pdev->dev, "Incorrect FS polarity value\n");
+		err = -EINVAL;
+		goto out1;
+	}
+
+	if (hw->data_polarity != TDM_POLAR_NEGATIVE &&
+	    hw->data_polarity != TDM_POLAR_POSITIV) {
+		dev_err(&pdev->dev, "Incorrect data polarity value\n");
+		err = -EINVAL;
+		goto out1;
+	}
+
+	/* Set controller data */
+	tdm->bus_num = pdev->id;
+	tdm->settings = hw;
+	tdm->setup_voice_channel = kirkwood_setup_voice_channel;
+	tdm->recv = kirkwood_recv;
+	tdm->send = kirkwood_send;
+	tdm->run_audio = kirkwood_tdm_run_audio;
+	tdm->stop_audio = kirkwood_tdm_stop_audio;
+	tdm->poll_rx = kirkwood_poll_rx;
+	tdm->poll_tx = kirkwood_poll_tx;
+
+	for (i = 0; i < KIRKWOOD_MAX_VOICE_CHANNELS; i++)
+	{
+	        ch = tdm_alloc_voice_channel();
+	        if (ch == NULL) {
+	                dev_err(&pdev->dev, "Can`t alloc voice channel %d\n", i);
+	                goto out1;
+	        }
+
+            tdm_register_new_voice_channel(tdm, ch, (void *)(onchip_tdm->voice_channels + i));
+	}
+
+
+	/* Get resources(memory, IRQ) associated with the device */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "Can`t request registers memory\n");
+		err = -ENODEV;
+		goto out2;
+	}
+
+	onchip_tdm->regs = ioremap(res->start, resource_size(res));
+	if (!onchip_tdm->regs) {
+		dev_err(&pdev->dev, "Incorrect setup registers memory area\n");
+		err = -EINVAL;
+		goto out2;
+	}
+
+	/* If need remap mbus windows */
+	if (hw->dram != NULL)
+		kirkwood_tdm_mbus_windows(onchip_tdm->regs, hw->dram);
+
+	/* Get IRQ number for all controllers events */
+	onchip_tdm->irq = platform_get_irq(pdev, 0);
+	if (onchip_tdm->irq < 0) {
+		dev_err(&pdev->dev, "Can`t request IRQ\n");
+		err = onchip_tdm->irq;
+		goto out3;
+	}
+
+	/* Set interrupt callback kirkwood_tdm_irq */
+	err = request_irq(onchip_tdm->irq, kirkwood_tdm_irq, IRQF_DISABLED,
+	                  dev_name(&pdev->dev), onchip_tdm);
+	if (err) {
+		dev_err(&pdev->dev, "Can`t setup IRQ callback\n");
+		err = -EINVAL;
+		goto out4;
+	}
+
+	onchip_tdm->controller_dev = &tdm->dev;
+
+	/* Initialization TDM controller */
+	err = kirkwood_tdm_hw_init(tdm);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Can't initialization tdm controller, %d\n",
+		        err);
+		goto out4;
+	}
+
+	err = tdm_controller_register(tdm);
+	if(err) {
+		dev_err(&pdev->dev, "cannot register tdm controller, %d\n", err);
+		goto out5;
+	}
+
+	printk("Init ok");
+
+	dev_dbg(&tdm->dev, "tdm controller registred sucessfully\n");
+	return 0;
+
+
+out5:
+	kirkwood_tdm_hw_deinit(tdm);
+
+out4:
+	free_irq(onchip_tdm->irq, onchip_tdm);
+
+out3:
+	if (onchip_tdm->regs)
+		iounmap(onchip_tdm->regs);
+
+out2:
+	tdm_free_voice_channels(tdm);
+
+out1:
+	tdm_free_controller(tdm);
+
+out0:
+	return err;
+}
+
+
+static int __exit kirkwood_tdm_remove(struct platform_device *pdev)
+{
+	struct tdm_controller *tdm = NULL;
+	struct kirkwood_tdm *onchip_tdm = NULL;
+
+	tdm = platform_get_drvdata(pdev);
+	if (tdm == NULL)
+		goto out0;
+
+	onchip_tdm = tdm_controller_get_devdata(tdm);
+
+	kirkwood_tdm_hw_deinit(tdm);
+	free_irq(onchip_tdm->irq, onchip_tdm);
+
+	tdm_free_voice_channels(tdm);
+	tdm_controller_unregister(tdm);
+	if (onchip_tdm->regs)
+		iounmap(onchip_tdm->regs);
+
+	tdm_free_controller(tdm);
+out0:
+	return 0;
+}
+
+
+static struct platform_driver kirkwood_tdm_driver = {
+	.probe	= kirkwood_tdm_probe,
+	.remove	= __exit_p(kirkwood_tdm_remove),
+	.driver = {
+		.name = "kirkwood_tdm",
+		.owner = THIS_MODULE,
+	},
+	.suspend = NULL,
+	.resume  = NULL,
+};
+
+
+static int __init kirkwood_init_tdm(void)
+{
+	return platform_driver_register(&kirkwood_tdm_driver);
+}
+
+static void __exit kirkwood_exit_tdm(void)
+{
+	platform_driver_unregister(&kirkwood_tdm_driver);
+}
+
+module_init(kirkwood_init_tdm);
+module_exit(kirkwood_exit_tdm);
+MODULE_AUTHOR("Michail Kurochkin <stelhs@yandex.ru>");
+MODULE_DESCRIPTION("TDM controller driver for Marvel kirkwood arch.");
+MODULE_LICENSE("GPL");
+
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/tdm/kirkwood_tdm.h linux-3.8-rc5/drivers/staging/tdm/kirkwood_tdm.h
--- linux-3.8-rc5-vanilla/drivers/staging/tdm/kirkwood_tdm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/tdm/kirkwood_tdm.h	2012-11-13 16:07:22.000000000 +0300
@@ -0,0 +1,110 @@
+/*
+ * kirkwood_tdm.h
+ *
+ *  Created on: 25.01.2012
+ *      Author: Michail Kurochkin
+ */
+
+#ifndef KIRKWOOD_TDM_H_
+#define KIRKWOOD_TDM_H_
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+struct kirkwood_tdm_regs {
+	u32 pcm_ctrl_reg; /*  0x0 PCM control register */
+	u32 chan_time_slot_ctrl; /*  0x4 Channel Time Slot Control Register */
+	u32 chan0_delay_ctrl; /*  0x8 Channel 0 Delay Control Register */
+	u32 chan1_delay_ctrl; /*  0xC Channel 1 Delay Control Register */
+	u32 chan0_enable_disable; /*  0x10 Channel 0 Enable and Disable Register */
+	u32 chan0_buff_ownership; /*  0x14 Channel 0 Buffer Ownership Register */
+	u32 chan0_transmit_start_addr; /*  0x18 Channel 0 Transmit Data Start Address Register */
+	u32 chan0_receive_start_addr; /*  0x1C Channel 0 Receive Data Start Address Register */
+	u32 chan1_enable_disable; /*  0x20 Channel 1 Enable and Disable Register */
+	u32 chan1_buff_ownership; /*  0x24 Channel 1 Buffer Ownership Register */
+	u32 chan1_transmit_start_addr; /*  0x28 Channel 1 Transmit Data Start Address Register */
+	u32 chan1_receive_start_addr; /*  0x2C Channel 1 Receive Data Start Address Register */
+	u32 chan0_total_sample; /*  0x30 Channel 0 Total Sample Count Register */
+	u32 chan1_total_sample; /*  0x34 Channel 1 Total Sample Count Register */
+	u32 num_time_slot; /*  0x38 Number of Time Slot Register */
+	u32 tdm_pcm_clock_div; /*  0x3C TDM PCM Clock Rate Divisor Register */
+	u32 int_event_mask; /*  0x40 Interrupt Event Mask Register */
+	u32 reserved_44h; /*  0x44 */
+	u32 int_status_mask; /*  0x48 Interrupt Status Mask Register */
+	u32 int_reset_sel; /*  0x4C Interrupt Reset Selection Register */
+	u32 int_status; /*  0x50 Interrupt Status Register */
+	u32 dummy_rx_write;/*  0x54 Dummy Data for Dummy RX Write Register */
+	u32 misc_control; /*  0x58 Miscellaneous Control Register */
+	u32 reserved_5Ch; /*  0x5C */
+	u32 chan0_current_tx_addr; /*  0x60 Channel 0 Transmit Data Current Address Register (for DMA) */
+	u32 chan0_current_rx_addr;  /*  0x64 Channel 0 Receive Data Current Address Register (for DMA) */
+	u32 chan1_current_tx_addr; /*  0x68 Channel 1 Transmit Data Current Address Register (for DMA) */
+	u32 chan1_current_rx_addr; /*  0x6C Channel 1 Receive Data Current Address Register (for DMA) */
+	u32 curr_time_slot; /*  0x70 Current Time Slot Register */
+	u32 revision; /*  0x74 TDM Revision Register */
+	u32 chan0_debug; /*  0x78 TDM Channel 0 Debug Register */
+	u32 chan1_debug; /*  0x7C TDM Channel 1 Debug Register */
+	u32 tdm_dma_abort_1; /*  0x80 TDM DMA Abort Register 1 */
+	u32 tdm_dma_abort_2; /*  0x84 TDM DMA Abort Register 2 */
+	u32 chan0_wideband_delay_ctrl; /*  0x88 TDM Channel 0 Wideband Delay Control Register */
+	u32 chan1_wideband_delay_ctrl; /*  0x8C TDM Channel 1 Wideband Delay Control Register */
+};
+
+
+#define KIRKWOOD_MAX_VOICE_CHANNELS 2 /*  Max count hardware channels */
+#define COUNT_DMA_BUFFERS_PER_CHANNEL 6 /*  Count of dma buffers for tx or rx path by one channel */
+
+/*
+ * Data specified for kirkwood hardware voice channel
+ */
+struct kirkwood_tdm_voice {
+	/* Transmitter and receiver buffers split to half.
+	 * While first half buffer is filling by DMA controller,
+	 * second half buffer is used by consumer and etc.
+	*/
+	u8 *tx_buf[COUNT_DMA_BUFFERS_PER_CHANNEL]; /*  transmitter voice buffers */
+	u8 *rx_buf[COUNT_DMA_BUFFERS_PER_CHANNEL]; /*  receiver voice buffers pointer */
+
+	dma_addr_t tx_buff_phy[COUNT_DMA_BUFFERS_PER_CHANNEL]; /*  two physical pointers to tx_buf */
+	dma_addr_t rx_buff_phy[COUNT_DMA_BUFFERS_PER_CHANNEL]; /*  two physical pointers to rx_buf */
+	atomic_t write_tx_buf_num; /*  current writing transmit buffer number */
+	atomic_t read_tx_buf_num; /*  current reading transmit buffer number */
+	atomic_t write_rx_buf_num; /*  current writing receive buffer number */
+	atomic_t read_rx_buf_num; /*  current reading receive buffer number */
+};
+
+
+/*
+ * Data specified for kirkwood tdm controller
+ */
+struct kirkwood_tdm {
+	struct kirkwood_tdm_regs *regs; /*  Registers for hardware TDM */
+	u32			irq; /*  Irq number for all TDM operations */
+
+	struct kirkwood_tdm_voice voice_channels[KIRKWOOD_MAX_VOICE_CHANNELS];
+	struct device *controller_dev;
+};
+
+
+#define TDM_WINDOW_CTRL(i)		(0x4030 + ((i) << 4))
+#define TDM_WINDOW_BASE(i)	(0x4034 + ((i) << 4))
+
+
+/* INT_STATUS_REG bits */
+#define RX_OVERFLOW_BIT(ch)	(1<<(0+(ch)*2))
+#define TX_UNDERFLOW_BIT(ch)	(1<<(1+((ch)*2)))
+#define RX_BIT(ch)		(1<<(4+((ch)*2)))
+#define TX_BIT(ch)		(1<<(5+((ch)*2)))
+#define RX_IDLE_BIT(ch)		(1<<(8+((ch)*2)))
+#define TX_IDLE_BIT(ch)		(1<<(9+((ch)*2)))
+#define RX_FIFO_FULL(ch)	(1<<(12+((ch)*2)))
+#define TX_FIFO_EMPTY(ch)	(1<<(13+((ch)*2)))
+#define DMA_ABORT_BIT		(1<<16)
+#define SLIC_INT_BIT		(1<<17)
+#define TDM_INT_TX(ch)	(TX_UNDERFLOW_BIT(ch) | TX_BIT(ch))
+#define TDM_INT_RX(ch)	(RX_OVERFLOW_BIT(ch) | RX_BIT(ch))
+
+
+#endif /* KIRKWOOD_TDM_H_ */
+
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/tdm/Makefile linux-3.8-rc5/drivers/staging/tdm/Makefile
--- linux-3.8-rc5-vanilla/drivers/staging/tdm/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/tdm/Makefile	2012-10-31 19:34:56.000000000 +0300
@@ -0,0 +1,19 @@
+#
+# Makefile for kernel TDM drivers.
+#
+
+ccflags-$(CONFIG_TDM_DEBUG) := -DDEBUG
+
+# small core, mostly translating board-specific
+# config declarations into driver model code
+obj-$(CONFIG_TDM)		+= tdm_core.o
+
+# TDM controller drivers (bus)
+obj-$(CONFIG_TDM_KIRKWOOD)			+= kirkwood_tdm.o
+
+# 	... add above this line ...
+
+# TDM protocol drivers (device/link on bus)
+#obj-$(CONFIG_TDM_TDMDEV)	+= tdmdev.o
+# 	... add above this line ...
+
diff -uprN -X linux-3.8-rc5-vanilla/Documentation/dontdiff linux-3.8-rc5-vanilla/drivers/staging/tdm/tdm_core.c linux-3.8-rc5/drivers/staging/tdm/tdm_core.c
--- linux-3.8-rc5-vanilla/drivers/staging/tdm/tdm_core.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-3.8-rc5/drivers/staging/tdm/tdm_core.c	2013-01-30 15:00:01.798052000 +0300
@@ -0,0 +1,826 @@
+/**********************************************************************
+ * Author: Michail Kurachkin
+ *
+ * Contact: michail.kurachkin@promwad.com
+ *
+ * Copyright (c) 2013 Promwad Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+**********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/tdm/tdm.h>
+
+#include <linux/cache.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+
+static void tdmdev_release(struct device *dev)
+{
+        struct tdm_device *tdm_dev = to_tdm_device(dev);
+
+        /* tdm controllers may cleanup for released devices */
+        if (tdm_dev->controller->cleanup)
+                tdm_dev->controller->cleanup(tdm_dev);
+
+        put_device(&tdm_dev->controller->dev);
+        kfree(tdm_dev);
+}
+
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+        const struct tdm_device *tdm_dev = to_tdm_device(dev);
+
+        return sprintf(buf, "%s\n", tdm_dev->modalias);
+}
+
+static struct device_attribute tdm_dev_attrs[] = {
+        __ATTR_RO(modalias),
+        __ATTR_NULL,
+};
+
+
+static int tdm_match_device(struct device *dev, struct device_driver *drv)
+{
+        const struct tdm_device *tdm_dev = to_tdm_device(dev);
+
+        return strcmp(tdm_dev->modalias, drv->name) == 0;
+}
+
+
+static int tdm_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+        const struct tdm_device *tdm_dev = to_tdm_device(dev);
+
+        add_uevent_var(env, "MODALIAS=%s%s", "tdm:", tdm_dev->modalias);
+        return 0;
+}
+
+
+static void tdm_release(struct device *dev)
+{
+        struct tdm_device *tdm_dev;
+
+        tdm_dev = to_tdm_device(dev);
+
+        kfree(tdm_dev);
+}
+
+
+static struct class tdm_class = {
+                .name           = "tdm",
+                .owner          = THIS_MODULE,
+                .dev_release    = tdm_release,
+        };
+
+
+
+/**
+ * Struct for TDM bus
+ */
+struct bus_type tdm_bus_type = {
+        .name           = "tdm",
+        .dev_attrs      = tdm_dev_attrs,
+        .match          = tdm_match_device,
+        .uevent         = tdm_uevent,
+        .suspend        = NULL,
+        .resume         = NULL,
+};
+EXPORT_SYMBOL_GPL(tdm_bus_type);
+
+
+
+static int tdm_drv_probe(struct device *dev)
+{
+        const struct tdm_driver *tdm_drv = to_tdm_driver(dev->driver);
+
+        return tdm_drv->probe(to_tdm_device(dev));
+}
+
+static int tdm_drv_remove(struct device *dev)
+{
+        const struct tdm_driver *tdm_drv = to_tdm_driver(dev->driver);
+
+        return tdm_drv->remove(to_tdm_device(dev));
+}
+
+static void tdm_drv_shutdown(struct device *dev)
+{
+        const struct tdm_driver *tdm_drv = to_tdm_driver(dev->driver);
+
+        tdm_drv->shutdown(to_tdm_device(dev));
+}
+
+
+/**
+ * tdm_register_driver - register a TDM driver
+ * @sdrv: the driver to register
+ * Context: can sleep
+ */
+int tdm_register_driver(struct tdm_driver *tdm_drv)
+{
+        tdm_drv->driver.bus = &tdm_bus_type;
+
+        if (tdm_drv->probe)
+                tdm_drv->driver.probe = tdm_drv_probe;
+
+        if (tdm_drv->remove)
+                tdm_drv->driver.remove = tdm_drv_remove;
+
+        if (tdm_drv->shutdown)
+                tdm_drv->driver.shutdown = tdm_drv_shutdown;
+
+        return driver_register(&tdm_drv->driver);
+}
+EXPORT_SYMBOL_GPL(tdm_register_driver);
+
+
+/**
+ * tdm_unregister_driver - reverse effect of tdm_register_driver
+ * @sdrv: the driver to unregister
+ * Context: can sleep
+ */
+void tdm_unregister_driver(struct tdm_driver *tdm_dev)
+{
+        if (tdm_dev)
+                driver_unregister(&tdm_dev->driver);
+}
+EXPORT_SYMBOL_GPL(tdm_unregister_driver);
+
+
+
+/**
+ * Request unused voice channel
+ * @param tdm_dev - TDM device requested voice channel
+ * @return pointer to voice channel
+ */
+struct tdm_voice_channel *request_voice_channel(struct tdm_device *tdm_dev) {
+        struct tdm_controller *tdm = tdm_dev->controller;
+        struct tdm_voice_channel *ch;
+        unsigned long flags;
+
+        spin_lock_irqsave(&tdm->lock, flags);
+
+	list_for_each_entry(ch, &tdm->voice_channels, list)
+                if (ch->dev == NULL) {
+                        ch->dev = &tdm_dev->dev;
+                        tdm_dev->ch = ch;
+                        spin_unlock_irqrestore(&tdm->lock, flags);
+                        return ch;
+                }
+
+        spin_unlock_irqrestore(&tdm->lock, flags);
+        return NULL;
+}
+
+/**
+ * Release requested voice channel
+ * @param TDM device requested early voice channel
+ * @return 0 - OK
+ */
+int release_voice_channel(struct tdm_device *tdm_dev)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+        struct tdm_voice_channel *ch;
+        unsigned long flags;
+
+        spin_lock_irqsave(&tdm->lock, flags);
+
+        tdm_dev->ch = NULL;
+	list_for_each_entry(ch, &tdm->voice_channels, list)
+                if (ch->dev == &tdm_dev->dev) {
+                        ch->dev = NULL;
+                        spin_unlock_irqrestore(&tdm->lock, flags);
+                        return 0;
+                }
+
+        spin_unlock_irqrestore(&tdm->lock, flags);
+
+        return -ENODEV;
+}
+
+
+
+/*
+ * Container for union board info of all TDM devices
+ */
+struct tdm_board_devices {
+        struct list_head        list;
+        struct tdm_board_info   bi; /* Board specific info */
+};
+
+/* List of all board specific TDM devices */
+static LIST_HEAD(tdm_devices_list);
+
+/* List of all registred TDM controllers */
+static LIST_HEAD(tdm_controller_list);
+
+
+/*
+ * Used to protect add/del opertion for board_info list and
+ * tdm_controller list, and their matching process
+ */
+static DEFINE_MUTEX(board_lock);
+
+
+static void tdm_match_controller_to_boardinfo(struct tdm_controller *tdm,
+        struct tdm_board_info *bi)
+{
+        struct tdm_device *tdm_dev;
+
+        if (tdm->bus_num != bi->bus_num)
+                return;
+
+        tdm_dev = tdm_new_device(tdm, bi);
+        if (!tdm_dev)
+                dev_err(tdm->dev.parent, "can't create new device for %s\n",
+                        bi->modalias);
+}
+
+
+
+/**
+ * Allocate memory for TDM controller device
+ * @dev: the controller, possibly using the platform_bus
+ * @size: how much zeroed driver-private data to allocate; the pointer to this
+ *      memory is in the driver_data field of the returned device,
+ *      accessible with tdm_controller_get_devdata().
+ * Context: can sleep
+ */
+struct tdm_controller *tdm_alloc_controller(struct device *dev, unsigned size) {
+        struct tdm_controller   *tdm;
+
+        if (!dev)
+                return NULL;
+
+        tdm = kzalloc(sizeof *tdm + size, GFP_KERNEL);
+        if (!tdm)
+                return NULL;
+
+        device_initialize(&tdm->dev);
+        tdm->dev.class = &tdm_class;
+        tdm->dev.parent = get_device(dev);
+        spin_lock_init(&tdm->lock);
+        INIT_LIST_HEAD(&tdm->list);
+        INIT_LIST_HEAD(&tdm->voice_channels);
+
+        dev_set_drvdata(&tdm->dev, tdm + 1);
+
+        return tdm;
+}
+EXPORT_SYMBOL_GPL(tdm_alloc_controller);
+
+
+/**
+ * Free memory for TDM controller device, allocated by tdm_alloc_controller
+ * @dev: the controller, possibly using the platform_bus
+ * Context: can sleep
+ */
+int tdm_free_controller(struct tdm_controller *tdm)
+{
+        if (!tdm)
+                return -EINVAL;
+
+        dev_set_drvdata(&tdm->dev, NULL);
+        put_device(&tdm->dev);
+        kzfree(tdm);
+
+        return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_free_controller);
+
+
+/**
+ * Allocate memory for voice channel
+ * @return object voice_channel or NULL if error
+ */
+struct tdm_voice_channel *tdm_alloc_voice_channel(void)
+{
+        struct tdm_voice_channel *ch;
+
+        ch = kzalloc(sizeof *ch, GFP_KERNEL);
+        if (!ch)
+                return NULL;
+
+        memset(ch, 0, sizeof *ch);
+        init_waitqueue_head(&ch->tx_queue);
+        init_waitqueue_head(&ch->rx_queue);
+
+        return ch;
+}
+EXPORT_SYMBOL_GPL(tdm_alloc_voice_channel);
+
+
+
+/**
+ * Add allocated voice channel in tdm controller
+ * @param tdm - tdm controller
+ * @param ch - allocated voice channel
+ * @param driver_private - private driver structure
+ * @return 0 - ok
+ */
+int
+tdm_register_new_voice_channel(struct tdm_controller *tdm,
+    struct tdm_voice_channel *ch,
+    void *driver_private)
+{
+	struct tdm_voice_channel *c;
+	int last_num;
+
+	last_num = -1;
+	list_for_each_entry(c, &tdm->voice_channels, list)
+		if (c->channel_num > last_num)
+			last_num = c->channel_num;
+
+	ch->channel_num = last_num + 1;
+	ch->private_data = driver_private;
+	list_add_tail(&ch->list, &tdm->voice_channels);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_register_new_voice_channel);
+
+/**
+ * free memory for voice channels allocated by tdm_alloc_voice_channels
+ * @param tdm - TDM controller
+ * @param count_channels - count supported voice channels
+ * @return
+ */
+int tdm_free_voice_channels(struct tdm_controller *tdm)
+{
+	struct tdm_voice_channel *ch, *ch_tmp;
+        if (!tdm)
+        	return -EINVAL;
+
+	list_for_each_entry_safe(ch, ch_tmp, &tdm->voice_channels, list)
+        {
+        	list_del(&ch->list);
+        	kzfree(ch);
+        }
+
+        return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_free_voice_channels);
+
+
+/**
+ * get voice_channel object by voice number
+ * @param tdm - tdm_controller contained voice channels
+ * @param num - voice channel number
+ * @return voice_channel object or NULL
+ */
+struct tdm_voice_channel *
+get_voice_channel_by_num(struct tdm_controller *tdm, int num)
+{
+	struct tdm_voice_channel *ch;
+	list_for_each_entry(ch, &tdm->voice_channels, list)
+		if (ch->channel_num == num)
+			return ch;
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(get_voice_channel_by_num);
+
+
+/**
+ * tdm_controller_register - register TDM controller
+ * @master: initialized master, originally from spi_alloc_master()
+ * Context: can sleep
+ *
+ * This must be called from context that can sleep.  It returns zero on
+ * success, else a negative error code (dropping the master's refcount).
+ * After a successful return, the caller is responsible for calling
+ * tdm_controller_unregister().
+ */
+int tdm_controller_register(struct tdm_controller *tdm)
+{
+        static atomic_t         dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
+        struct device           *dev = tdm->dev.parent;
+        struct tdm_board_devices        *bi;
+        int                     status = -ENODEV;
+        int                     dynamic = 0;
+
+        if (!dev) {
+                dev_err(dev, "parent device not exist\n");
+                return -ENODEV;
+        }
+
+        /* convention:  dynamically assigned bus IDs count down from the max */
+        if (tdm->bus_num < 0) {
+                tdm->bus_num = atomic_dec_return(&dyn_bus_id);
+                dynamic = 1;
+        }
+
+        /* register the device, then userspace will see it.
+         * registration fails if the bus ID is in use.
+         */
+        dev_set_name(&tdm->dev, "tdm%u", tdm->bus_num);
+        status = device_add(&tdm->dev);
+        if (status < 0) {
+                dev_err(dev, "cannot added controller device, %d\n", status);
+                goto done;
+        }
+        dev_dbg(dev, "registered controller %s%s\n", dev_name(&tdm->dev),
+                dynamic ? " (dynamic)" : "");
+
+        mutex_lock(&board_lock);
+        list_add_tail(&tdm->list, &tdm_controller_list);
+        list_for_each_entry(bi, &tdm_devices_list, list)
+                tdm_match_controller_to_boardinfo(tdm, &bi->bi);
+
+        mutex_unlock(&board_lock);
+
+        status = 0;
+
+done:
+        return status;
+}
+EXPORT_SYMBOL_GPL(tdm_controller_register);
+
+
+static int __unregister(struct device *dev, void *null)
+{
+        device_unregister(dev);
+        return 0;
+}
+
+
+/**
+ * tdm_controller_unregister - unregister TDM controller
+ * @tdm: the controller being unregistered
+ * Context: can sleep
+ *
+ * This call is used only by TDM controller drivers, which are the
+ * only ones directly touching chip registers.
+ *
+ * This must be called from context that can sleep.
+ */
+void tdm_controller_unregister(struct tdm_controller *tdm)
+{
+        int dummy;
+
+        mutex_lock(&board_lock);
+        list_del(&tdm->list);
+        mutex_unlock(&board_lock);
+
+        dummy = device_for_each_child(&tdm->dev, NULL, __unregister);
+        device_unregister(&tdm->dev);
+}
+EXPORT_SYMBOL_GPL(tdm_controller_unregister);
+
+
+/**
+ * tdm_new_device - instantiate one new TDM device
+ * @tdm: TDM Controller to which device is connected
+ * @chip: Describes the TDM device
+ * Context: can sleep
+ *
+ * Returns the new device, or NULL.
+ */
+struct tdm_device *tdm_new_device(struct tdm_controller *tdm,
+                                  struct tdm_board_info *chip) {
+        struct tdm_device       *tdm_dev;
+        int                     status;
+
+        if (!tdm_controller_get(tdm))
+                return NULL;
+
+        tdm_dev = kzalloc(sizeof *tdm_dev, GFP_KERNEL);
+        if (!tdm_dev) {
+                dev_err(tdm->dev.parent, "cannot alloc for TDM device\n");
+                tdm_controller_put(tdm);
+                return NULL;
+        }
+
+        tdm_dev->controller = tdm;
+        tdm_dev->dev.parent = tdm->dev.parent;
+        tdm_dev->dev.bus = &tdm_bus_type;
+        tdm_dev->dev.release = tdmdev_release;
+        device_initialize(&tdm_dev->dev);
+
+        WARN_ON(strlen(chip->modalias) >= sizeof(tdm_dev->modalias));
+
+        tdm_dev->tdm_channel_num = chip->tdm_channel_num;
+        tdm_dev->mode_wideband = chip->mode_wideband;
+        tdm_dev->buffer_sample_count = chip->buffer_sample_count;
+        strlcpy(tdm_dev->modalias, chip->modalias, sizeof(tdm_dev->modalias));
+
+        status = tdm_add_device(tdm_dev);
+        if (status < 0) {
+                put_device(&tdm_dev->dev);
+                return NULL;
+        }
+
+        return tdm_dev;
+}
+EXPORT_SYMBOL_GPL(tdm_new_device);
+
+
+
+/**
+ * tdm_add_device - Add tdm_device on TDM bus
+ * @tdm_dev: TDM device to register
+ *
+ * Returns 0 on success; negative errno on failure
+ */
+int tdm_add_device(struct tdm_device *tdm_dev)
+{
+        static DEFINE_MUTEX(tdm_add_lock);
+        struct tdm_controller *tdm = tdm_dev->controller;
+        struct tdm_controller_hw_settings *hw = tdm->settings;
+        struct device *dev = tdm->dev.parent;
+        struct device *d;
+        struct tdm_voice_channel *ch;
+        int status;
+        u8 count_tdm_channels;
+
+
+        if (tdm_dev->mode_wideband) {
+                dev_err(dev, "mode_wideband is not supported by this driver\n");
+                status = -EINVAL;
+                goto done;
+        }
+
+        count_tdm_channels = hw->count_time_slots / hw->channel_size;
+
+        if (tdm_dev->tdm_channel_num >= count_tdm_channels) {
+                dev_err(dev, "Incorrect requested TDM channel.\n"
+                        "Requested %d TDM channel, %d TDM channels available.\n",
+                        tdm_dev->tdm_channel_num, count_tdm_channels);
+
+                status = -EINVAL;
+                goto done;
+        }
+
+        if (tdm_dev->mode_wideband &&
+            (tdm_dev->tdm_channel_num > count_tdm_channels / 2)) {
+                dev_err(dev, "Incorrect requested TDM channel in wideband mode.\n"
+                        "Requested %d TDM channel, %d TDM channels available\n"
+                        "in wideband mode\n",
+                        tdm_dev->tdm_channel_num, count_tdm_channels / 2);
+
+                status = -EINVAL;
+                goto done;
+        }
+
+
+        /* Set the bus ID string */
+        dev_set_name(&tdm_dev->dev, "%s.%u", dev_name(&tdm_dev->controller->dev),
+                     tdm_dev->tdm_channel_num);
+
+        /* We need to make sure there's no other device with this
+         * chipselect **BEFORE** we call setup(), else we'll trash
+         * its configuration.  Lock against concurrent add() calls.
+         */
+        mutex_lock(&tdm_add_lock);
+
+        d = bus_find_device_by_name(&tdm_bus_type, NULL, dev_name(&tdm_dev->dev));
+        if (d != NULL) {
+                dev_err(dev, "TDM channel %d already in use\n",
+                        tdm_dev->tdm_channel_num);
+                put_device(d);
+                status = -EBUSY;
+                goto done;
+        }
+
+        ch = request_voice_channel(tdm_dev);
+        if (ch == NULL) {
+                dev_err(dev, "Can't request TDM voice channel. All voice channels is busy\n");
+                status = -EBUSY;
+                goto done;
+        }
+
+        printk("ch = %s\n", dev_name(ch->dev));
+
+        /* Configuring voice channel */
+        ch->mode_wideband = tdm_dev->mode_wideband;
+        ch->tdm_channel = tdm_dev->tdm_channel_num;
+
+        /* Run setup voice channel */
+        status = tdm_dev->controller->setup_voice_channel(ch);
+        if (status < 0) {
+                dev_err(dev, "can't setup voice channel, status %d\n", status);
+                goto done;
+        }
+
+        /* Device may be bound to an active driver when this returns */
+        status = device_add(&tdm_dev->dev);
+        if (status < 0)
+                dev_err(dev, "can't add %s, status %d\n",
+                        dev_name(&tdm_dev->dev), status);
+        else
+                dev_dbg(dev, "registered child %s\n", dev_name(&tdm_dev->dev));
+
+done:
+        mutex_unlock(&tdm_add_lock);
+        return status;
+}
+EXPORT_SYMBOL_GPL(tdm_add_device);
+
+
+
+/**
+ * Receive audio-data from tdm device.
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @param data - pointer to receive block data.
+ *              Allocated data size must be equal value
+ *              returned by tdm_get_voice_block_size();
+ * @return 0 - success
+ */
+int tdm_recv(struct tdm_device *tdm_dev, u8 *data)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        if (tdm_dev->ch == NULL)
+                return -ENODEV;
+
+        return tdm->recv(tdm_dev->ch, data);
+}
+EXPORT_SYMBOL_GPL(tdm_recv);
+
+
+/**
+ * Transmit audio-data from tdm device.
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @param data - pointer to transmit block data.
+ *              Transmit data size must be equal value
+ *              returned by tdm_get_voice_block_size();
+ * @return 0 - success
+ */
+int tdm_send(struct tdm_device *tdm_dev, u8 *data)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        if (tdm_dev->ch == NULL)
+                return -ENODEV;
+
+        return tdm->send(tdm_dev->ch, data);
+}
+EXPORT_SYMBOL_GPL(tdm_send);
+
+
+/**
+ * Enable audio transport
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - success
+ */
+int tdm_run_audio(struct tdm_device *tdm_dev)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        if (!tdm_dev->ch) {
+                dev_err(&tdm_dev->dev, "Can't run audio because not allocated tdm voice channel\n");
+                return -ENODEV;
+        }
+
+        return tdm->run_audio(tdm_dev);
+}
+EXPORT_SYMBOL_GPL(tdm_run_audio);
+
+
+/**
+ * Disable audio transport
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - success
+ */
+int tdm_stop_audio(struct tdm_device *tdm_dev)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        if (!tdm_dev->ch) {
+                dev_err(&tdm_dev->dev, "Can't stop audio because not allocated tdm voice channel\n");
+                return -ENODEV;
+        }
+
+        return tdm->stop_audio(tdm_dev);
+}
+EXPORT_SYMBOL_GPL(tdm_stop_audio);
+
+
+
+/**
+ * Check rx audio buffer for exist new data
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - not enought data, 1 - data exist
+ */
+int tdm_poll_rx(struct tdm_device *tdm_dev)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        return tdm->poll_rx(tdm_dev);
+}
+EXPORT_SYMBOL_GPL(tdm_poll_rx);
+
+
+/**
+ * Check tx audio buffer for free space
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return 0 - not enought free space, 1 - exist free space
+ */
+int tdm_poll_tx(struct tdm_device *tdm_dev)
+{
+        struct tdm_controller *tdm = tdm_dev->controller;
+
+        return tdm->poll_tx(tdm_dev);
+}
+EXPORT_SYMBOL_GPL(tdm_poll_tx);
+
+
+/**
+ * Get voice block size for transmit or receive operations
+ * @param tdm_dev - tdm device registered on TDM bus
+ * @return voice block size, or error if returned value less 0
+ */
+int tdm_get_voice_block_size(struct tdm_device *tdm_dev)
+{
+        struct tdm_voice_channel *ch;
+
+        ch = tdm_dev->ch;
+        if (ch == NULL)
+                return -ENODEV;
+
+        return ch->buffer_len;
+}
+EXPORT_SYMBOL_GPL(tdm_get_voice_block_size);
+
+
+
+/**
+ * tdm_register_board_info - register TDM devices for a given board
+ * @info: array of chip descriptors
+ * @n: how many descriptors are provided
+ * Context: can sleep
+ */
+int __init
+tdm_register_board_info(struct tdm_board_info const *info, unsigned n)
+{
+        struct tdm_board_devices *bi;
+        int i;
+
+        bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
+        if (!bi)
+                return -ENOMEM;
+
+        for (i = 0; i < n; i++, bi++, info++) {
+                struct tdm_controller *tdm;
+
+                memcpy(&bi->bi, info, sizeof(*info));
+                mutex_lock(&board_lock);
+
+                list_add_tail(&bi->list, &tdm_devices_list);
+                list_for_each_entry(tdm, &tdm_controller_list, list)
+                        tdm_match_controller_to_boardinfo(tdm, &bi->bi);
+
+                mutex_unlock(&board_lock);
+        }
+
+        return 0;
+}
+
+
+
+static int __init tdm_core_init(void)
+{
+        int status;
+
+        status = bus_register(&tdm_bus_type);
+        if (status < 0)
+                goto err0;
+
+        status = class_register(&tdm_class);
+        if (status < 0)
+                goto err1;
+        return 0;
+
+err1:
+        bus_unregister(&tdm_bus_type);
+err0:
+        return status;
+}
+
+postcore_initcall(tdm_core_init);
+
