diff mbox

[2/3,RFC] TDM Framework

Message ID 1331384221-29661-1-git-send-email-poonam.aggrwal@freescale.com (mailing list archive)
State RFC
Headers show

Commit Message

poonam aggrwal March 10, 2012, 12:57 p.m. UTC
From: Sandeep Singh <Sandeep@freescale.com>

TDM Framework is an attempt to provide a platform independent layer which
can offer a standard interface  for TDM access to different client modules.
Beneath, the framework layer can house different types of TDM drivers to handle
various TDM devices, the hardware intricacies of the devices being completely
taken care by TDM drivers.

This framework layer will allow any type of TDM device to hook with it.
For example Freescale controller as on MPC8315, UCC based TDM controller, or 
any other controller.

The main functions of this Framework are:
- provides interface to TDM clients to access TDM functionalities.
- provides standard interface for TDM drivers to hook with the framework. 
- handles various data handling stuff and buffer management.

In future this Framework will be extended to provide Interface for Line control
devices also. For example SLIC, E1/T1 Framers etc.

Limitations/Future Work
---------------------------
1. Presently the framework supports only Single Port channelised mode.
2. Also the configurability options are limited which will be extended later on.
3. Only kernel mode TDM clients are supported currently. Support for User mode
clients will be added later. 

Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
---
 A couple of todos' are left in the patch, we are working on it and will be
addressed in the updated patch set.
 drivers/Kconfig                 |    1 +
 drivers/Makefile                |    1 +
 drivers/tdm/Kconfig             |   25 +
 drivers/tdm/tdm-core.c          | 1146 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |   11 +
 include/linux/tdm.h             |  347 ++++++++++++
 6 files changed, 1531 insertions(+), 0 deletions(-)
 create mode 100644 drivers/tdm/Kconfig
 create mode 100644 drivers/tdm/tdm-core.c
 create mode 100644 include/linux/tdm.h

Comments

Aggrwal Poonam-B10812 April 23, 2012, 8 a.m. UTC | #1
> -----Original Message-----
> From: Aggrwal Poonam-B10812
> Sent: Saturday, March 10, 2012 6:27 PM
> To: linuxppc-dev@lists.ozlabs.org
> Cc: Singh Sandeep-B37400; Aggrwal Poonam-B10812
> Subject: [PATCH][2/3][RFC] TDM Framework
> 

Any feedback on this patchset?

> From: Sandeep Singh <Sandeep@freescale.com>
> 
> TDM Framework is an attempt to provide a platform independent layer which
> can offer a standard interface  for TDM access to different client
> modules.
> Beneath, the framework layer can house different types of TDM drivers to
> handle various TDM devices, the hardware intricacies of the devices being
> completely taken care by TDM drivers.
> 
> This framework layer will allow any type of TDM device to hook with it.
> For example Freescale controller as on MPC8315, UCC based TDM controller,
> or any other controller.
> 
> The main functions of this Framework are:
> - provides interface to TDM clients to access TDM functionalities.
> - provides standard interface for TDM drivers to hook with the framework.
> - handles various data handling stuff and buffer management.
> 
> In future this Framework will be extended to provide Interface for Line
> control devices also. For example SLIC, E1/T1 Framers etc.
> 
> Limitations/Future Work
> ---------------------------
> 1. Presently the framework supports only Single Port channelised mode.
> 2. Also the configurability options are limited which will be extended
> later on.
> 3. Only kernel mode TDM clients are supported currently. Support for User
> mode clients will be added later.
> 
> Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
> Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
> ---
>  A couple of todos' are left in the patch, we are working on it and will
> be addressed in the updated patch set.
>  drivers/Kconfig                 |    1 +
>  drivers/Makefile                |    1 +
>  drivers/tdm/Kconfig             |   25 +
>  drivers/tdm/tdm-core.c          | 1146
> +++++++++++++++++++++++++++++++++++++++
>  include/linux/mod_devicetable.h |   11 +
>  include/linux/tdm.h             |  347 ++++++++++++
>  6 files changed, 1531 insertions(+), 0 deletions(-)  create mode 100644
> drivers/tdm/Kconfig  create mode 100644 drivers/tdm/tdm-core.c  create
> mode 100644 include/linux/tdm.h
> 
> diff --git a/drivers/Kconfig b/drivers/Kconfig index ad6c1eb..25f7f5b
> 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
> 
>  source "drivers/net/dpa/NetCommSw/Kconfig"
> 
> +source "drivers/tdm/Kconfig"
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile index cd546eb..362b5ed
> 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+= infiniband/
>  obj-$(CONFIG_SGI_SN)		+= sn/
>  obj-y				+= firmware/
>  obj-$(CONFIG_CRYPTO)		+= crypto/
> +obj-$(CONFIG_TDM)		+= tdm/
>  obj-$(CONFIG_SUPERH)		+= sh/
>  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
>  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
> diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig new file mode
> 100644 index 0000000..8db2b05
> --- /dev/null
> +++ b/drivers/tdm/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# TDM subsystem configuration
> +#
> +
> +menuconfig TDM
> +	tristate "TDM support"
> +	---help---
> +	  More information is contained in the directory
> <file:Documentation/tdm/>,
> +	  especially in the file called "summary" there.
> +	  If you want TDM support, you should say Y here and also to the
> +	  specific driver for your bus adapter(s) below.
> +
> +	  This TDM support can also be built as a module.  If so, the
> module
> +	  will be called tdm-core.
> +
> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going
> on.
> +
> +endif # TDM
> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c new file
> mode 100644 index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program is free software; you can redistribute  it and/or
> +modify it
> + * under  the terms of  the GNU General  Public License as published by
> +the
> + * Free Software Foundation;  either version 2 of the  License, or (at
> +your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License
> +along
> + * with this program; if not, write  to the Free Software Foundation,
> +Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"
> +
> +
> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data = 1;
> +
> +/* this tasklet is created for each adapter instance */ static void
> +tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */ static int
> +tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap) {
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +	adap->drv_count++;
> +
> +	if (!adap->tasklet_conf) {
> +		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
> +						(unsigned long)adap);
> +		adap->tasklet_conf = 1;
> +	}
> +
> +	return TDM_E_OK;
> +}
> +
> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;
> +
> +	/* If no more driver is registed with the adapter*/
> +	if (!adap->drv_count && adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	if (driver->detach_adapter) {
> +		if (driver->detach_adapter(adap))
> +			pr_err("detach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +
> +	driver->adapter = NULL;
> +	return res;
> +}
> +
> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap) {
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration
> */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
> +				driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +
> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> + * @adapter: the adapter to add
> + * Context: can sleep
> + *
> + * This routine is used to declare a TDM adapter
> + * When this returns zero, a new device number will be allocated and
> +stored
> + * in adap->id, and the specified adapter became available for the
> clients.
> + * Otherwise, a negative errno value is returned.
> + */
> +int tdm_add_adapter(struct tdm_adapter *adapter) {
> +	int id, res = TDM_E_OK;
> +	if (!adapter) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +retry:
> +	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
> +		return -ENOMEM;
> +
> +	mutex_lock(&tdm_core_lock);
> +	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	if (res < 0) {
> +		if (res == -EAGAIN)
> +			goto retry;
> +		return res;
> +	}
> +
> +	adapter->id = id;
> +	return tdm_register_adapter(adapter);
> +}
> +EXPORT_SYMBOL(tdm_add_adapter);
> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap) {
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	pr_debug("adapter [%s] unregistered\n", adap->name);
> +
> +	list_del(&adap->list);
> +	/* Clear the device structure in case this adapter is ever going to
> be
> +	   added again */
> +	adap->parent = NULL;
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_del_adapter);
> +
> +/* TDM Client Drivers Registration/De-registration Functions */ int
> +tdm_register_driver(struct tdm_driver *driver) {
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM
> +framework
> + * @driver: the driver being unregistered  */ void
> +tdm_unregister_driver(struct tdm_driver *driver) {
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */
> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name); }
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}
> +
> +/* We must initialize early, because some subsystems register tdm
> +drivers
> + * in subsys_initcall() code, but are linked (and initialized) before
> tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);
> +
> +
> +/* Interface to the tdm device/adapter */
> +
> +/* tdm_adap_send - issue a TDM write
> + * @adap: Handle to TDM device
> + * @buf: Data that will be written to the TDM device
> + * @count: How many bytes to write
> + *
> + * Returns negative errno, or else the number of bytes written.
> + */
> +int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count) {
> +	int res;
> +
> +	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (adap->algo->tdm_write)
> +		res = adap->algo->tdm_write(adap, buf, count);
> +	else {
> +		pr_err("TDM level write not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* If everything went ok (i.e. frame transmitted), return #bytes
> +	   transmitted, else error code. */
> +	return (res == 1) ? count : res;
> +}
> +EXPORT_SYMBOL(tdm_adap_send);
> +
> +/**
> + * tdm_adap_recv - issue a TDM read
> + * @adap: Handle to TDM device
> + * @buf: Where to store data read from TDM device
> + *
> + * Returns negative errno, or else the number of bytes read.
> + */
> +int tdm_adap_recv(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_read)
> +		res = adap->algo->tdm_read(adap, (u16 **)buf);
> +	else {
> +		pr_err("TDM level read not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. frame received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +
> +/**
> + * tdm_adap_get_write_buf - get next write TDM device buffer
> + * @adap: Handle to TDM device
> + * @buf: pointer to TDM device buffer
> + *
> + * Returns negative errno, or else size of the write buffer.
> + */
> +int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_get_write_buf) {
> +		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
> +	} else {
> +		pr_err("TDM level write buf get not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. 1 msg received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_get_write_buf);
> +
> +int tdm_adap_enable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_enable) {
> +		res = adap->algo->tdm_enable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_enable);
> +
> +int tdm_adap_disable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_disable) {
> +		res = adap->algo->tdm_disable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_disable);
> +
> +struct tdm_adapter *tdm_get_adapter(int id) {
> +	struct tdm_adapter *adapter;
> +
> +	mutex_lock(&tdm_core_lock);
> +	adapter = idr_find(&tdm_adapter_idr, id);
> +	if (adapter && !try_module_get(adapter->owner))
> +		adapter = NULL;
> +
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return adapter;
> +}
> +EXPORT_SYMBOL(tdm_get_adapter);
> +
> +void tdm_put_adapter(struct tdm_adapter *adap) {
> +	module_put(adap->owner);
> +}
> +EXPORT_SYMBOL(tdm_put_adapter);
> +
> +
> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port) {
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;
> +
> +	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
> +	port->driver = driver;
> +	port->adapter = adap;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_add_tail(&port->list, &adap->myports);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	INIT_LIST_HEAD(&port->mychannels);
> +
> +	*h_port = port;
> +
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_open);
> +
> +unsigned int tdm_port_close(void *h_port) {
> +	struct tdm_adapter *adap;
> +	struct tdm_driver *driver;
> +	struct tdm_port *port;
> +	struct tdm_channel *temp, *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	driver =  port->driver;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}
> +	adap = driver->adapter;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_del(&port->list);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	if (port->p_port_data != NULL) {
> +		int i;
> +		struct tdm_bd *ch_bd;
> +
> +		/* If the tdm is in channelised mode,
> +		de-allocate the channelised buffer */
> +		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		kfree(port->p_port_data);
> +	}
> +	kfree(port);
> +	return res;
> +out:
> +	if (port)
> +		kfree(port->p_port_data);
> +	kfree(port);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_close);
> +
> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> flags);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_read);
> +
> +
> +unsigned int tdm_channel_write(void *h_port, void *h_channel,
> +				void *p_data, u16 size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *tx_bd;
> +	unsigned long flags;
> +	int err = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel; #ifdef TDM_CORE_DEBUG
> +	bool data_flag = 0;
> +#endif
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	if (p_data == NULL) { /* invalid data*/
> +		pr_err("Invalid Data");
> +		return -EFAULT;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
> +	tx_bd = channel->p_ch_data->tx_in_data;
> +
> +	if (!tx_bd->flag) {
> +		tx_bd->length = size;
> +		memcpy(tx_bd->p_data, p_data,
> +			size * port->adapter->adapt_cfg.slot_width);
> +		tx_bd->flag = 1;
> +		tx_bd->offset = 0;
> +		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
> +				channel->p_ch_data->tx_data_fifo : tx_bd+1;
> +		port->port_stat.tx_pkt_count++;
> +#ifdef TDM_CORE_DEBUG
> +		data_flag = 1;
> +#endif
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> +						flags);
> +		port->port_stat.tx_pkt_drop_count++;
> +		pr_err("E_NO_MEMORY -Failed Transmit");
> +		return -ENOMEM;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> flags);
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		int k;
> +		pr_info("\nTX port:%d - Write - Port TX-%d\n",
> +						port->port_id, size);
> +		for (k = 0; k < size; k++)
> +			pr_info("%x", p_data[k]);
> +		pr_info("\n");
> +	}
> +#endif
> +	return err;
> +}
> +EXPORT_SYMBOL(tdm_channel_write);
> +
> +wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port) {
> +	struct tdm_port *port;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return NULL;
> +	}
> +
> +	return &port->ch_wait_queue;
> +
> +}
> +EXPORT_SYMBOL(tdm_port_get_wait_queue);
> +
> +/* Driver Function for select and poll. Based on Port no, it sleeps on
> + * waitqueue */
> +unsigned int tdm_port_poll(void *h_port, unsigned int wait_time) {
> +	struct tdm_port *port;
> +	unsigned long timeout = msecs_to_jiffies(wait_time);
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	if (!port->p_port_data || !port->in_use)
> +		return -EIO;
> +
> +	if (port->p_port_data->rx_out_data->flag) {
> +		pr_debug("Data Available");
> +		return TDM_E_OK;
> +	}
> +	if (timeout) {
> +		wait_event_interruptible_timeout(port->ch_wait_queue,
> +					  port->p_port_data->rx_out_data->flag,
> +					  timeout);
> +
> +		if (port->p_port_data->rx_out_data->flag) {
> +			pr_debug("Data Available");
> +			return TDM_E_OK;
> +		}
> +	}
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(tdm_port_poll);
> +
> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats
> +*portStat) {
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}
> +
> +		if (ch_data) {
> +			/*	Wake up the Port Data Poll event */
> +			wake_up_interruptible(&port->ch_wait_queue);
> +#ifdef	TDM_CORE_DEBUG
> +			pr_info("Port RX-%d-%d\n", channel->ch_id,
> ch_data_len);
> +			for (i = 0; i < ch_data_len; i++)
> +				pr_info("%x", pcm_buffer[i]);
> +			pr_info("\n");
> +#endif
> +			port->port_stat.rx_pkt_count++;
> +			ch_data = 0;
> +		}
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_data_tx_interleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	bool last_data = 0;
> +	u16 *output_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +#ifdef TDM_CORE_DEBUG
> +	u8	data_flag = 0;
> +#endif
> +
> +	if (adap == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	buf_size = tdm_adap_get_write_buf(adap, (void
> **)&output_tdm_buffer);
> +	if (buf_size <= 0 || !output_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset =
> +bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
> +
> +
> +	memset(output_tdm_buffer, 0, sizeof(buf_size));
> +
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* check if the port is open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +								list) {
> +		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
> +
> +
> +		/* if the channel is open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +
> +		spin_lock(&channel->p_ch_data->tx_channel_lock);
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +			ch_bd = channel->p_ch_data->tx_out_data;
> +			if (ch_bd->flag) {
> +				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
> +						ch_bd->offset);
> +				/*if the buffer has less frames than required */
> +				if (frame_ch_data_size >=
> +					((ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width))) {
> +					ch_data_len =
> +					(ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width);
> +					last_data = 1;
> +				} else {
> +					ch_data_len = frame_ch_data_size;
> +				}
> +				/* Interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					/* TODO- need to be generic for any size
> +					   assignment*/
> +					output_tdm_buffer[channel->ch_id +
> +						bytes_slot_offset * i] =
> +								pcm_buffer[i];
> +				}
> +				/* If all the data of this buffer is
> +							transmitted */
> +				if (last_data) {
> +					ch_bd->flag = 0;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->tx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->tx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.tx_pkt_conf_count++;
> +				} else {
> +					ch_bd->offset += ch_data_len *
> +						(adap->adapt_cfg.slot_width);
> +				}
> +#ifdef	TDM_CORE_DEBUG
> +				data_flag = 1;
> +#endif
> +			}
> +		spin_unlock(&channel->p_ch_data->tx_channel_lock);
> +		}
> +	}
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		pr_info("TX-TDM Interleaved Data-\n");
> +		for (i = 0; i < 64; i++)
> +			pr_info("%x", output_tdm_buffer[i]);
> +		pr_info("\n");
> +	  }
> +#endif
> +	return TDM_E_OK;
> +}
> +
> +/* Channel Level APIs of TDM Framework */ int tdm_channel_open(u16
> +chanid, u16 ch_width, struct tdm_port *port,
> +				void **h_channel)
> +{
> +	struct tdm_channel *channel, *temp;
> +	unsigned long		flags;
> +	struct tdm_ch_data	*p_ch_data;
> +	int res = TDM_E_OK;
> +
> +	if (!(port && h_channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +		if (channel->ch_id == chanid) {
> +			pr_err("%s: Channel %d already open\n",
> +						__func__, chanid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> +	if (!channel) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
> +	if (!p_ch_data) {
> +		res = -ENOMEM;
> +		goto outdata;
> +	}
> +
> +	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
> +	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
> +
> +	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
> +	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
> +	spin_lock_init(&p_ch_data->rx_channel_lock);
> +	spin_lock_init(&p_ch_data->tx_channel_lock);
> +
> +	channel->p_ch_data = p_ch_data;
> +
> +	channel->ch_id = chanid;
> +	channel->ch_cfg.first_slot = chanid;
> +	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized
> mode and
> +						configurable for other modes */
> +	channel->port = port;
> +	channel->in_use = 1;
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_add_tail(&channel->list, &port->mychannels);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +	*h_channel = channel;
> +
> +	return res;
> +
> +outdata:
> +	kfree(channel);
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_open);
> +
> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_del(&channel->list);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +out:
> +	if (channel)
> +		kfree(channel->p_ch_data);
> +	kfree(channel);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_close);
> +
> +void init_config_adapter(struct tdm_adapter *adap) {
> +	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
> +		.loopback = e_TDM_PROCESS_NORMAL,
> +		.num_ch = NUM_CHANNELS,
> +		.ch_size_type = CHANNEL_16BIT_LIN,
> +		.frame_len = NUM_SAMPLES_PER_FRAME,
> +		.num_frames = NUM_SAMPLES_PER_FRAME,
> +		.adap_mode = e_TDM_ADAPTER_MODE_NONE
> +			 };
> +
> +	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 +
> 1;
> +
> +	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
> +		sizeof(struct fsl_tdm_adapt_cfg));
> +
> +	return;
> +}
> +EXPORT_SYMBOL(init_config_adapter);
> +
> +static void tdm_data_tasklet_fn(unsigned long data) {
> +	struct tdm_adapter *adapter;
> +	adapter = (struct tdm_adapter *)data;
> +	if (adapter != NULL) {
> +		tdm_data_tx_interleave(adapter);
> +		tdm_data_rx_deinterleave(adapter);
> +	}
> +}
> +
> +
> +MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
> +	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
> +MODULE_DESCRIPTION("TDM Driver Framework Core"); MODULE_LICENSE("GPL");
> diff --git a/include/linux/mod_devicetable.h
> b/include/linux/mod_devicetable.h index ae28e93..dc1a655 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -416,6 +416,17 @@ struct i2c_device_id {
>  			__attribute__((aligned(sizeof(kernel_ulong_t))));
>  };
> 
> +/* tdm */
> +
> +#define TDM_NAME_SIZE   20
> +#define TDM_MODULE_PREFIX "tdm:"
> +
> +struct tdm_device_id {
> +	char name[TDM_NAME_SIZE];
> +	kernel_ulong_t driver_data      /* Data private to the driver */
> +			__attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
> +
>  /* spi */
> 
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/tdm.h b/include/linux/tdm.h new file mode
> 100644 index 0000000..8cf4ef5
> --- /dev/null
> +++ b/include/linux/tdm.h
> @@ -0,0 +1,347 @@
> +/* include/linux/tdm.h
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * tdm.h - definitions for the tdm-device framework interface
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * This program is free software; you can redistribute  it and/or
> +modify it
> + * under  the terms of  the GNU General  Public License as published by
> +the
> + * Free Software Foundation;  either version 2 of the  License, or (at
> +your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License
> +along
> + * with this program; if not, write  to the Free Software Foundation,
> +Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /*
> Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3
> +
> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment
> +*/ static inline int ALIGN_SIZE(u64 size, u32 alignment) {
> +	return (size + alignment - 1) & (~(alignment - 1)); }
> +
> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)  */ struct
> +tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific
> functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);
> +
> +	const struct tdm_device_id *id_table;
> +
> +	/* The associated adapter for this driver */
> +	struct tdm_adapter *adapter;
> +	struct list_head list;
> +};
> +
> +/* tdm per port statistics structure, used for providing and storing
> +tdm port
> + * statistics.
> + */
> +struct tdm_port_stats {
> +	unsigned int rx_pkt_count;	/* Rx frame count per channel */
> +	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
> +					 clean space for new buffer */
> +	unsigned int tx_pkt_count;	/* Tx frame count per channel */
> +	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count
> per
> +					 channel */
> +	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel
> due to
> +					 queue full */
> +};
> +
> +
> +/* tdm Buffer Descriptor, used for Creating Interleaved and
> +De-interleaved
> + * FIFOs
> + */
> +struct tdm_bd {
> +	unsigned char flag;		/* BD is full or empty */
> +	unsigned char wrap;		/* BD is last in the queue */
> +	unsigned short length;	/* Length of Data in BD */
> +	/*TODO: use dyanmic memory */
> +	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
> +	unsigned long offset;	/* Offset of the Data Pointer to be used */
> +};
> +
> +#define TDM_CH_RX_BD_RING_SIZE	3
> +#define TDM_CH_TX_BD_RING_SIZE	3
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_port_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel
> Data
> +								BD Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled
> by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
> +							read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel
> Data
> +								BD Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
> +						 filled by App */
> +	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read
> by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
> +};
> +
> +/* structure tdm_port_cfg - contains configuration params for a port */
> +struct tdm_port_cfg {
> +	unsigned short port_mode;
> +};
> +
> +/* struct tdm_port - represent an TDM ports for a device */ struct
> +tdm_port {
> +	unsigned short port_id;
> +	unsigned short in_use;		/* Port is enabled? */
> +	uint16_t rx_max_frames;		/* Received Port frames
> +					 before allowing Read Operation in
> +					 Port Mode */
> +
> +	struct tdm_port_stats port_stat;/* A structure parameters defining
> +					 TDM port statistics. */
> +	struct tdm_port_data *p_port_data;	/* a structure parameters
> +						defining tdm channelised data */
> +	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data
> */
> +
> +	struct tdm_driver *driver;	/* driver for this port */
> +	struct tdm_adapter *adapter;	/* adapter for this port */
> +	struct list_head list;		/* list of ports */
> +	struct list_head mychannels;	/* list of channels, created on this
> +					 port*/
> +	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
> +	struct tdm_port_cfg port_cfg;/* A structure parameters defining
> +					 TDM port configuration. */
> +};
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_ch_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data
> BD
> +								Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App
> */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data
> BD
> +								Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
> +						App */
> +	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
> +};
> +
> +/* Channel config params */
> +struct tdm_ch_cfg {
> +	unsigned short num_slots;
> +	unsigned short first_slot;
> +};
> +
> +/* struct tdm_channel- represent a TDM channel for a port */ struct
> +tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for
> channel */
> +};
> +
> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *); };
> +
> +/* tdm_adapter_mode is to define in mode of the device */ enum
> +tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to
> +operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or
> +internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};
> +
> +
> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */
> +	u32 num_frames;
> +	u8 loopback;			/* loopback or normal */
> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */
> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */
> +	int max_timeslots;		/* Max Number of timeslots that are
> +					supported on this adapter */
> +};
> +
> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device
> +along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */
> +	unsigned int class;	/* classes to allow probing for */
> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to
> access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/
> +
> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to
> perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;
> +
> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;
> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> +	void *data;
> +	struct fsl_tdm_adapt_cfg adapt_cfg;
> +};
> +
> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev) {
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void
> +*data) {
> +	dev->data = data;
> +}
> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *); extern int
> +tdm_del_adapter(struct tdm_adapter *); extern int
> +tdm_register_driver(struct tdm_driver *); extern void
> +tdm_del_driver(struct tdm_driver *); extern void
> +tdm_unregister_driver(struct tdm_driver *); extern void
> +init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **); extern
> +unsigned int tdm_port_close(void *); extern unsigned int
> +tdm_port_ioctl(void *, unsigned int, unsigned long); extern unsigned
> +int tdm_channel_read(void *, void *, void *, u16 *); extern unsigned
> +int tdm_channel_write(void *, void * , void *, u16); extern unsigned
> +int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);
> +
> +static inline int tdm_add_driver(struct tdm_driver *driver) {
> +	return tdm_register_driver(driver);
> +}
> +
> +extern struct tdm_adapter *tdm_get_adapter(int id); extern void
> +tdm_put_adapter(struct tdm_adapter *adap);
> +
> +#endif /* __KERNEL__ */
> +
> +#define TDM_E_OK 0
> +
> +#endif /* _LINUX_TDM_H */
> --
> 1.6.5.6
Scott Wood April 24, 2012, 12:34 a.m. UTC | #2
On 03/10/2012 06:57 AM, Poonam Aggrwal wrote:
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index ad6c1eb..25f7f5b 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>  
> +source "drivers/tdm/Kconfig"
>  endmenu

When posting patches to this list, please ensure that they are based on
an upstream Linux kernel and not a Freescale BSP kernel.

> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going on.
> +
> +endif # TDM

Please use the normal kernel mechanisms for controlling debug messages.

> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
> new file mode 100644
> index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License along
> + * with this program; if not, write  to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"

What is a reference to tdm_fsl.h doing in the presumably
hardware-independent tdm-core.c?

> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data = 1;

Describe when one would want to change this, and provide a better way to
change it (e.g. module parameter or sysfs knob).

/*
 * Linux kernel
 * multi-line comment style
 * is like this.
 */

> +/* this tasklet is created for each adapter instance */
> +static void tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */
> +static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> +{
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}

s/TDM_E_OK/0/g

> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;

Why can't one driver service multiple adapters?  How would multiple
drivers service one adapter?  Are there sub-devices that need individual
controlling?

> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);

Why ignore errors?

> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;

Shouldn't this be an error?

> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;

If the driver doesn't have a detach_adapter method, you skip
decrementing the count and leave the tasklet lying around?

> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}

Pointers aren't handles.  The caller should not be passing NULL, and it
would be more useful to crash and get a backtrace if it does.  It's not
realistic to check every pointer for being NULL when there's no
legitimate reason it could be NULL, and it doesn't help you if you have
some other bad value besides NULL.

> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);

This is too noisy.  You haven't even gotten a match yet.

> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number

Why are there device numbers at all?

I suspect there's a fair bit of copy and paste going on of another
subsystem's quirks (i2c?).  I don't see any mention in the copyright
block of this code having been derived from anything else, though...

> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);

Why are you dropping the lock then reacquiring it?

> +/* TDM Client Drivers Registration/De-registration Functions */
> +int tdm_register_driver(struct tdm_driver *driver)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}

Indentation.

> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM framework
> + * @driver: the driver being unregistered
> + */
> +void tdm_unregister_driver(struct tdm_driver *driver)
> +{
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */

Whitespace.

> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
> +}
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}

Don't spam the console just because the driver got loaded or unloaded
(at this point you haven't even found the hardware).

> +/* We must initialize early, because some subsystems register tdm drivers
> + * in subsys_initcall() code, but are linked (and initialized) before tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);

tdm_init doesn't do anything, so why does it need to run early?

> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)

Why is the return unsigned int?  You're returning negative numbers.

Also consider having the return be a pointer, and use PTR_ERR/ERR_PTR --
or at least put a proper type on h_port (what is the "h_"?).

> +{
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}

Either make these pr_debug (or remove them), or make the message more
specific.

> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;

When would a port have in_use be false, other than this brief window
where nothing else should be looking at it?

> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}

Broken indentation.

> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;

Unnecessary casts.

> +	if ((port && channel) == 0) { /* invalid handle*/

This is an odd construct -- how about "if (!port || !channel)"?

> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);

Shouldn't you test whether it's in use after you get the lock?

> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}

That pr_info() is inappropriate.  This driver appears to be overly
chatty in general (and with quite vague messages) -- or is this debug
stuff that will be removed in a non-RFC patch?

> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
> +{
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}

Broken indentation.  Spaces around operators.

> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}

close() seems an odd time to be checking for supported channel width,
especially since you don't use that variable anywhere else in this function.

> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__

Is this supposed to be a userspace header ever?  If TDM exposes a
userspace interface, it needs to be documented.

> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3

These need proper namespacing -- plus, should all of these really be
hardcoded like this?  Is this standard stuff that will always be the same?

> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment */
> +static inline int ALIGN_SIZE(u64 size, u32 alignment)
> +{
> +	return (size + alignment - 1) & (~(alignment - 1));
> +}

Use the already existing ALIGN().

> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)
> + */
> +struct tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);

Please describe what semantics you expect for the non-standard functions.

Where are the "ioctl like commands" defined?  When would they be used?
I don't see it used in this patchset.

> +/* struct tdm_channel- represent a TDM channel for a port */
> +struct tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
> +};

Why are ch_id and especially in_use u16?

> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *);
> +};
> 

Provide parameter names and document the semantics you're expecting.

> +/* tdm_adapter_mode is to define in mode of the device */
> +enum tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};

Commas go at the end of lines, not the beginning.

No hungarian notation -- drop the "e_".

> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */

Is u8 really appropriate here?  Maybe int?

What does "type" mean in "ch_size_type"?

> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */

Use #defines or an enum.

> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */

If it's not used, why is it here?

> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */

What does the id mean?  Why do we need an arbitrary numberspace?

> +	unsigned int class;	/* classes to allow probing for */

What sort of values would go here?

> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/

Why is the parent device not used?

> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;

What does "tdm_rx_flag" indicate?  What about "tasklet_conf"?

> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;

If you've got more than one list, it's probably a bad idea for any of
them to be simply called "list".

> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */

Comments should add new information, not just restate what the code
already said.

> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
> +{
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
> +{
> +	dev->data = data;
> +}

Is this really needed?

> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *);
> +extern int tdm_del_adapter(struct tdm_adapter *);
> +extern int tdm_register_driver(struct tdm_driver *);
> +extern void tdm_del_driver(struct tdm_driver *);
> +extern void tdm_unregister_driver(struct tdm_driver *);
> +extern void init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> +extern unsigned int tdm_port_close(void *);
> +extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
> +extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
> +extern unsigned int tdm_channel_write(void *, void * , void *, u16);
> +extern unsigned int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);

Please provide parameter names with prototypes.

The "extern" is unnecessary.

> +static inline int tdm_add_driver(struct tdm_driver *driver)
> +{
> +	return tdm_register_driver(driver);
> +}

What is the semantic difference between tdm_add_driver() and
tdm_register_driver()?  Why do they both exist?

-Scott
Benjamin Herrenschmidt April 25, 2012, 5:49 a.m. UTC | #3
On Sat, 2012-03-10 at 18:27 +0530, Poonam Aggrwal wrote:
> From: Sandeep Singh <Sandeep@freescale.com>
> 
> TDM Framework is an attempt to provide a platform independent layer which
> can offer a standard interface  for TDM access to different client modules.
> Beneath, the framework layer can house different types of TDM drivers to handle
> various TDM devices, the hardware intricacies of the devices being completely
> taken care by TDM drivers.

Neither the changeset comment, the code, not the Documentation file
(which are non-existent, at least in this patch, though mentioned),
define what "TDM" actually is :-)

Cheers,
Ben.


> This framework layer will allow any type of TDM device to hook with it.
> For example Freescale controller as on MPC8315, UCC based TDM controller, or 
> any other controller.
> 
> The main functions of this Framework are:
> - provides interface to TDM clients to access TDM functionalities.
> - provides standard interface for TDM drivers to hook with the framework. 
> - handles various data handling stuff and buffer management.
> 
> In future this Framework will be extended to provide Interface for Line control
> devices also. For example SLIC, E1/T1 Framers etc.
> 
> Limitations/Future Work
> ---------------------------
> 1. Presently the framework supports only Single Port channelised mode.
> 2. Also the configurability options are limited which will be extended later on.
> 3. Only kernel mode TDM clients are supported currently. Support for User mode
> clients will be added later. 
> 
> Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
> Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
> ---
>  A couple of todos' are left in the patch, we are working on it and will be
> addressed in the updated patch set.
>  drivers/Kconfig                 |    1 +
>  drivers/Makefile                |    1 +
>  drivers/tdm/Kconfig             |   25 +
>  drivers/tdm/tdm-core.c          | 1146 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mod_devicetable.h |   11 +
>  include/linux/tdm.h             |  347 ++++++++++++
>  6 files changed, 1531 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/tdm/Kconfig
>  create mode 100644 drivers/tdm/tdm-core.c
>  create mode 100644 include/linux/tdm.h
> 
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index ad6c1eb..25f7f5b 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>  
> +source "drivers/tdm/Kconfig"
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index cd546eb..362b5ed 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+= infiniband/
>  obj-$(CONFIG_SGI_SN)		+= sn/
>  obj-y				+= firmware/
>  obj-$(CONFIG_CRYPTO)		+= crypto/
> +obj-$(CONFIG_TDM)		+= tdm/
>  obj-$(CONFIG_SUPERH)		+= sh/
>  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
>  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
> diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig
> new file mode 100644
> index 0000000..8db2b05
> --- /dev/null
> +++ b/drivers/tdm/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# TDM subsystem configuration
> +#
> +
> +menuconfig TDM
> +	tristate "TDM support"
> +	---help---
> +	  More information is contained in the directory <file:Documentation/tdm/>,
> +	  especially in the file called "summary" there.
> +	  If you want TDM support, you should say Y here and also to the
> +	  specific driver for your bus adapter(s) below.
> +
> +	  This TDM support can also be built as a module.  If so, the module
> +	  will be called tdm-core.
> +
> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going on.
> +
> +endif # TDM
> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
> new file mode 100644
> index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License along
> + * with this program; if not, write  to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"
> +
> +
> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data = 1;
> +
> +/* this tasklet is created for each adapter instance */
> +static void tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */
> +static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> +{
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +	adap->drv_count++;
> +
> +	if (!adap->tasklet_conf) {
> +		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
> +						(unsigned long)adap);
> +		adap->tasklet_conf = 1;
> +	}
> +
> +	return TDM_E_OK;
> +}
> +
> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;
> +
> +	/* If no more driver is registed with the adapter*/
> +	if (!adap->drv_count && adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	if (driver->detach_adapter) {
> +		if (driver->detach_adapter(adap))
> +			pr_err("detach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +
> +	driver->adapter = NULL;
> +	return res;
> +}
> +
> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
> +				driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +
> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> + * @adapter: the adapter to add
> + * Context: can sleep
> + *
> + * This routine is used to declare a TDM adapter
> + * When this returns zero, a new device number will be allocated and stored
> + * in adap->id, and the specified adapter became available for the clients.
> + * Otherwise, a negative errno value is returned.
> + */
> +int tdm_add_adapter(struct tdm_adapter *adapter)
> +{
> +	int id, res = TDM_E_OK;
> +	if (!adapter) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +retry:
> +	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
> +		return -ENOMEM;
> +
> +	mutex_lock(&tdm_core_lock);
> +	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	if (res < 0) {
> +		if (res == -EAGAIN)
> +			goto retry;
> +		return res;
> +	}
> +
> +	adapter->id = id;
> +	return tdm_register_adapter(adapter);
> +}
> +EXPORT_SYMBOL(tdm_add_adapter);
> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	pr_debug("adapter [%s] unregistered\n", adap->name);
> +
> +	list_del(&adap->list);
> +	/* Clear the device structure in case this adapter is ever going to be
> +	   added again */
> +	adap->parent = NULL;
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_del_adapter);
> +
> +/* TDM Client Drivers Registration/De-registration Functions */
> +int tdm_register_driver(struct tdm_driver *driver)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM framework
> + * @driver: the driver being unregistered
> + */
> +void tdm_unregister_driver(struct tdm_driver *driver)
> +{
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */
> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
> +}
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}
> +
> +/* We must initialize early, because some subsystems register tdm drivers
> + * in subsys_initcall() code, but are linked (and initialized) before tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);
> +
> +
> +/* Interface to the tdm device/adapter */
> +
> +/* tdm_adap_send - issue a TDM write
> + * @adap: Handle to TDM device
> + * @buf: Data that will be written to the TDM device
> + * @count: How many bytes to write
> + *
> + * Returns negative errno, or else the number of bytes written.
> + */
> +int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count)
> +{
> +	int res;
> +
> +	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (adap->algo->tdm_write)
> +		res = adap->algo->tdm_write(adap, buf, count);
> +	else {
> +		pr_err("TDM level write not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* If everything went ok (i.e. frame transmitted), return #bytes
> +	   transmitted, else error code. */
> +	return (res == 1) ? count : res;
> +}
> +EXPORT_SYMBOL(tdm_adap_send);
> +
> +/**
> + * tdm_adap_recv - issue a TDM read
> + * @adap: Handle to TDM device
> + * @buf: Where to store data read from TDM device
> + *
> + * Returns negative errno, or else the number of bytes read.
> + */
> +int tdm_adap_recv(struct tdm_adapter *adap, void **buf)
> +{
> +	int res;
> +
> +	if (adap->algo->tdm_read)
> +		res = adap->algo->tdm_read(adap, (u16 **)buf);
> +	else {
> +		pr_err("TDM level read not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. frame received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +
> +/**
> + * tdm_adap_get_write_buf - get next write TDM device buffer
> + * @adap: Handle to TDM device
> + * @buf: pointer to TDM device buffer
> + *
> + * Returns negative errno, or else size of the write buffer.
> + */
> +int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf)
> +{
> +	int res;
> +
> +	if (adap->algo->tdm_get_write_buf) {
> +		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
> +	} else {
> +		pr_err("TDM level write buf get not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. 1 msg received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_get_write_buf);
> +
> +int tdm_adap_enable(struct tdm_driver *drv)
> +{
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_enable) {
> +		res = adap->algo->tdm_enable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_enable);
> +
> +int tdm_adap_disable(struct tdm_driver *drv)
> +{
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_disable) {
> +		res = adap->algo->tdm_disable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_disable);
> +
> +struct tdm_adapter *tdm_get_adapter(int id)
> +{
> +	struct tdm_adapter *adapter;
> +
> +	mutex_lock(&tdm_core_lock);
> +	adapter = idr_find(&tdm_adapter_idr, id);
> +	if (adapter && !try_module_get(adapter->owner))
> +		adapter = NULL;
> +
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return adapter;
> +}
> +EXPORT_SYMBOL(tdm_get_adapter);
> +
> +void tdm_put_adapter(struct tdm_adapter *adap)
> +{
> +	module_put(adap->owner);
> +}
> +EXPORT_SYMBOL(tdm_put_adapter);
> +
> +
> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)
> +{
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;
> +
> +	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
> +	port->driver = driver;
> +	port->adapter = adap;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_add_tail(&port->list, &adap->myports);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	INIT_LIST_HEAD(&port->mychannels);
> +
> +	*h_port = port;
> +
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_open);
> +
> +unsigned int tdm_port_close(void *h_port)
> +{
> +	struct tdm_adapter *adap;
> +	struct tdm_driver *driver;
> +	struct tdm_port *port;
> +	struct tdm_channel *temp, *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	driver =  port->driver;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}
> +	adap = driver->adapter;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_del(&port->list);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	if (port->p_port_data != NULL) {
> +		int i;
> +		struct tdm_bd *ch_bd;
> +
> +		/* If the tdm is in channelised mode,
> +		de-allocate the channelised buffer */
> +		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		kfree(port->p_port_data);
> +	}
> +	kfree(port);
> +	return res;
> +out:
> +	if (port)
> +		kfree(port->p_port_data);
> +	kfree(port);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_close);
> +
> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock, flags);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_read);
> +
> +
> +unsigned int tdm_channel_write(void *h_port, void *h_channel,
> +				void *p_data, u16 size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *tx_bd;
> +	unsigned long flags;
> +	int err = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +#ifdef TDM_CORE_DEBUG
> +	bool data_flag = 0;
> +#endif
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	if (p_data == NULL) { /* invalid data*/
> +		pr_err("Invalid Data");
> +		return -EFAULT;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
> +	tx_bd = channel->p_ch_data->tx_in_data;
> +
> +	if (!tx_bd->flag) {
> +		tx_bd->length = size;
> +		memcpy(tx_bd->p_data, p_data,
> +			size * port->adapter->adapt_cfg.slot_width);
> +		tx_bd->flag = 1;
> +		tx_bd->offset = 0;
> +		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
> +				channel->p_ch_data->tx_data_fifo : tx_bd+1;
> +		port->port_stat.tx_pkt_count++;
> +#ifdef TDM_CORE_DEBUG
> +		data_flag = 1;
> +#endif
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> +						flags);
> +		port->port_stat.tx_pkt_drop_count++;
> +		pr_err("E_NO_MEMORY -Failed Transmit");
> +		return -ENOMEM;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock, flags);
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		int k;
> +		pr_info("\nTX port:%d - Write - Port TX-%d\n",
> +						port->port_id, size);
> +		for (k = 0; k < size; k++)
> +			pr_info("%x", p_data[k]);
> +		pr_info("\n");
> +	}
> +#endif
> +	return err;
> +}
> +EXPORT_SYMBOL(tdm_channel_write);
> +
> +wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port)
> +{
> +	struct tdm_port *port;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return NULL;
> +	}
> +
> +	return &port->ch_wait_queue;
> +
> +}
> +EXPORT_SYMBOL(tdm_port_get_wait_queue);
> +
> +/* Driver Function for select and poll. Based on Port no, it sleeps on
> + * waitqueue */
> +unsigned int tdm_port_poll(void *h_port, unsigned int wait_time)
> +{
> +	struct tdm_port *port;
> +	unsigned long timeout = msecs_to_jiffies(wait_time);
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	if (!port->p_port_data || !port->in_use)
> +		return -EIO;
> +
> +	if (port->p_port_data->rx_out_data->flag) {
> +		pr_debug("Data Available");
> +		return TDM_E_OK;
> +	}
> +	if (timeout) {
> +		wait_event_interruptible_timeout(port->ch_wait_queue,
> +					  port->p_port_data->rx_out_data->flag,
> +					  timeout);
> +
> +		if (port->p_port_data->rx_out_data->flag) {
> +			pr_debug("Data Available");
> +			return TDM_E_OK;
> +		}
> +	}
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(tdm_port_poll);
> +
> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
> +{
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}
> +
> +		if (ch_data) {
> +			/*	Wake up the Port Data Poll event */
> +			wake_up_interruptible(&port->ch_wait_queue);
> +#ifdef	TDM_CORE_DEBUG
> +			pr_info("Port RX-%d-%d\n", channel->ch_id, ch_data_len);
> +			for (i = 0; i < ch_data_len; i++)
> +				pr_info("%x", pcm_buffer[i]);
> +			pr_info("\n");
> +#endif
> +			port->port_stat.rx_pkt_count++;
> +			ch_data = 0;
> +		}
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_data_tx_interleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	bool last_data = 0;
> +	u16 *output_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +#ifdef TDM_CORE_DEBUG
> +	u8	data_flag = 0;
> +#endif
> +
> +	if (adap == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	buf_size = tdm_adap_get_write_buf(adap, (void **)&output_tdm_buffer);
> +	if (buf_size <= 0 || !output_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
> +
> +
> +	memset(output_tdm_buffer, 0, sizeof(buf_size));
> +
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* check if the port is open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +								list) {
> +		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
> +
> +
> +		/* if the channel is open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +
> +		spin_lock(&channel->p_ch_data->tx_channel_lock);
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +			ch_bd = channel->p_ch_data->tx_out_data;
> +			if (ch_bd->flag) {
> +				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
> +						ch_bd->offset);
> +				/*if the buffer has less frames than required */
> +				if (frame_ch_data_size >=
> +					((ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width))) {
> +					ch_data_len =
> +					(ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width);
> +					last_data = 1;
> +				} else {
> +					ch_data_len = frame_ch_data_size;
> +				}
> +				/* Interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					/* TODO- need to be generic for any size
> +					   assignment*/
> +					output_tdm_buffer[channel->ch_id +
> +						bytes_slot_offset * i] =
> +								pcm_buffer[i];
> +				}
> +				/* If all the data of this buffer is
> +							transmitted */
> +				if (last_data) {
> +					ch_bd->flag = 0;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->tx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->tx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.tx_pkt_conf_count++;
> +				} else {
> +					ch_bd->offset += ch_data_len *
> +						(adap->adapt_cfg.slot_width);
> +				}
> +#ifdef	TDM_CORE_DEBUG
> +				data_flag = 1;
> +#endif
> +			}
> +		spin_unlock(&channel->p_ch_data->tx_channel_lock);
> +		}
> +	}
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		pr_info("TX-TDM Interleaved Data-\n");
> +		for (i = 0; i < 64; i++)
> +			pr_info("%x", output_tdm_buffer[i]);
> +		pr_info("\n");
> +	  }
> +#endif
> +	return TDM_E_OK;
> +}
> +
> +/* Channel Level APIs of TDM Framework */
> +int tdm_channel_open(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				void **h_channel)
> +{
> +	struct tdm_channel *channel, *temp;
> +	unsigned long		flags;
> +	struct tdm_ch_data	*p_ch_data;
> +	int res = TDM_E_OK;
> +
> +	if (!(port && h_channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +		if (channel->ch_id == chanid) {
> +			pr_err("%s: Channel %d already open\n",
> +						__func__, chanid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> +	if (!channel) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
> +	if (!p_ch_data) {
> +		res = -ENOMEM;
> +		goto outdata;
> +	}
> +
> +	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
> +	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
> +
> +	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
> +	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
> +	spin_lock_init(&p_ch_data->rx_channel_lock);
> +	spin_lock_init(&p_ch_data->tx_channel_lock);
> +
> +	channel->p_ch_data = p_ch_data;
> +
> +	channel->ch_id = chanid;
> +	channel->ch_cfg.first_slot = chanid;
> +	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized mode and
> +						configurable for other modes */
> +	channel->port = port;
> +	channel->in_use = 1;
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_add_tail(&channel->list, &port->mychannels);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +	*h_channel = channel;
> +
> +	return res;
> +
> +outdata:
> +	kfree(channel);
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_open);
> +
> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_del(&channel->list);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +out:
> +	if (channel)
> +		kfree(channel->p_ch_data);
> +	kfree(channel);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_close);
> +
> +void init_config_adapter(struct tdm_adapter *adap)
> +{
> +	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
> +		.loopback = e_TDM_PROCESS_NORMAL,
> +		.num_ch = NUM_CHANNELS,
> +		.ch_size_type = CHANNEL_16BIT_LIN,
> +		.frame_len = NUM_SAMPLES_PER_FRAME,
> +		.num_frames = NUM_SAMPLES_PER_FRAME,
> +		.adap_mode = e_TDM_ADAPTER_MODE_NONE
> +			 };
> +
> +	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 + 1;
> +
> +	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
> +		sizeof(struct fsl_tdm_adapt_cfg));
> +
> +	return;
> +}
> +EXPORT_SYMBOL(init_config_adapter);
> +
> +static void tdm_data_tasklet_fn(unsigned long data)
> +{
> +	struct tdm_adapter *adapter;
> +	adapter = (struct tdm_adapter *)data;
> +	if (adapter != NULL) {
> +		tdm_data_tx_interleave(adapter);
> +		tdm_data_rx_deinterleave(adapter);
> +	}
> +}
> +
> +
> +MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
> +	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
> +MODULE_DESCRIPTION("TDM Driver Framework Core");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index ae28e93..dc1a655 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -416,6 +416,17 @@ struct i2c_device_id {
>  			__attribute__((aligned(sizeof(kernel_ulong_t))));
>  };
>  
> +/* tdm */
> +
> +#define TDM_NAME_SIZE   20
> +#define TDM_MODULE_PREFIX "tdm:"
> +
> +struct tdm_device_id {
> +	char name[TDM_NAME_SIZE];
> +	kernel_ulong_t driver_data      /* Data private to the driver */
> +			__attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
> +
>  /* spi */
>  
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/tdm.h b/include/linux/tdm.h
> new file mode 100644
> index 0000000..8cf4ef5
> --- /dev/null
> +++ b/include/linux/tdm.h
> @@ -0,0 +1,347 @@
> +/* include/linux/tdm.h
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * tdm.h - definitions for the tdm-device framework interface
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License along
> + * with this program; if not, write  to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3
> +
> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment */
> +static inline int ALIGN_SIZE(u64 size, u32 alignment)
> +{
> +	return (size + alignment - 1) & (~(alignment - 1));
> +}
> +
> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)
> + */
> +struct tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);
> +
> +	const struct tdm_device_id *id_table;
> +
> +	/* The associated adapter for this driver */
> +	struct tdm_adapter *adapter;
> +	struct list_head list;
> +};
> +
> +/* tdm per port statistics structure, used for providing and storing tdm port
> + * statistics.
> + */
> +struct tdm_port_stats {
> +	unsigned int rx_pkt_count;	/* Rx frame count per channel */
> +	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
> +					 clean space for new buffer */
> +	unsigned int tx_pkt_count;	/* Tx frame count per channel */
> +	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count per
> +					 channel */
> +	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel due to
> +					 queue full */
> +};
> +
> +
> +/* tdm Buffer Descriptor, used for Creating Interleaved and De-interleaved
> + * FIFOs
> + */
> +struct tdm_bd {
> +	unsigned char flag;		/* BD is full or empty */
> +	unsigned char wrap;		/* BD is last in the queue */
> +	unsigned short length;	/* Length of Data in BD */
> +	/*TODO: use dyanmic memory */
> +	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
> +	unsigned long offset;	/* Offset of the Data Pointer to be used */
> +};
> +
> +#define TDM_CH_RX_BD_RING_SIZE	3
> +#define TDM_CH_TX_BD_RING_SIZE	3
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_port_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel Data
> +								BD Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
> +							read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel Data
> +								BD Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
> +						 filled by App */
> +	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
> +};
> +
> +/* structure tdm_port_cfg - contains configuration params for a port */
> +struct tdm_port_cfg {
> +	unsigned short port_mode;
> +};
> +
> +/* struct tdm_port - represent an TDM ports for a device */
> +struct tdm_port {
> +	unsigned short port_id;
> +	unsigned short in_use;		/* Port is enabled? */
> +	uint16_t rx_max_frames;		/* Received Port frames
> +					 before allowing Read Operation in
> +					 Port Mode */
> +
> +	struct tdm_port_stats port_stat;/* A structure parameters defining
> +					 TDM port statistics. */
> +	struct tdm_port_data *p_port_data;	/* a structure parameters
> +						defining tdm channelised data */
> +	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data */
> +
> +	struct tdm_driver *driver;	/* driver for this port */
> +	struct tdm_adapter *adapter;	/* adapter for this port */
> +	struct list_head list;		/* list of ports */
> +	struct list_head mychannels;	/* list of channels, created on this
> +					 port*/
> +	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
> +	struct tdm_port_cfg port_cfg;/* A structure parameters defining
> +					 TDM port configuration. */
> +};
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_ch_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data BD
> +								Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data BD
> +								Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
> +						App */
> +	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
> +};
> +
> +/* Channel config params */
> +struct tdm_ch_cfg {
> +	unsigned short num_slots;
> +	unsigned short first_slot;
> +};
> +
> +/* struct tdm_channel- represent a TDM channel for a port */
> +struct tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
> +};
> +
> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *);
> +};
> +
> +/* tdm_adapter_mode is to define in mode of the device */
> +enum tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};
> +
> +
> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */
> +	u32 num_frames;
> +	u8 loopback;			/* loopback or normal */
> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */
> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */
> +	int max_timeslots;		/* Max Number of timeslots that are
> +					supported on this adapter */
> +};
> +
> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */
> +	unsigned int class;	/* classes to allow probing for */
> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/
> +
> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;
> +
> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;
> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> +	void *data;
> +	struct fsl_tdm_adapt_cfg adapt_cfg;
> +};
> +
> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
> +{
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
> +{
> +	dev->data = data;
> +}
> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *);
> +extern int tdm_del_adapter(struct tdm_adapter *);
> +extern int tdm_register_driver(struct tdm_driver *);
> +extern void tdm_del_driver(struct tdm_driver *);
> +extern void tdm_unregister_driver(struct tdm_driver *);
> +extern void init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> +extern unsigned int tdm_port_close(void *);
> +extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
> +extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
> +extern unsigned int tdm_channel_write(void *, void * , void *, u16);
> +extern unsigned int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);
> +
> +static inline int tdm_add_driver(struct tdm_driver *driver)
> +{
> +	return tdm_register_driver(driver);
> +}
> +
> +extern struct tdm_adapter *tdm_get_adapter(int id);
> +extern void tdm_put_adapter(struct tdm_adapter *adap);
> +
> +#endif /* __KERNEL__ */
> +
> +#define TDM_E_OK 0
> +
> +#endif /* _LINUX_TDM_H */
Sandeep Singh April 26, 2012, 11:20 a.m. UTC | #4
-----Original Message-----
From: Benjamin Herrenschmidt [mailto:benh@kernel.crashing.org] 
Sent: Wednesday, April 25, 2012 11:20 AM
To: Aggrwal Poonam-B10812
Cc: linuxppc-dev@lists.ozlabs.org; Singh Sandeep-B37400
Subject: Re: [PATCH][2/3][RFC] TDM Framework

On Sat, 2012-03-10 at 18:27 +0530, Poonam Aggrwal wrote:
> From: Sandeep Singh <Sandeep@freescale.com>
> 
> TDM Framework is an attempt to provide a platform independent layer 
> which can offer a standard interface  for TDM access to different client modules.
> Beneath, the framework layer can house different types of TDM drivers 
> to handle various TDM devices, the hardware intricacies of the devices 
> being completely taken care by TDM drivers.

Neither the changeset comment, the code, not the Documentation file (which are non-existent, at least in this patch, though mentioned), define what "TDM" actually is :-)

[Sandeep] Thanks for your comments. Documentation for TDM is present in the following patch:

http://patchwork.ozlabs.org/patch/145857/

Regards,
Sandeep

Cheers,
Ben.


> This framework layer will allow any type of TDM device to hook with it.
> For example Freescale controller as on MPC8315, UCC based TDM 
> controller, or any other controller.
> 
> The main functions of this Framework are:
> - provides interface to TDM clients to access TDM functionalities.
> - provides standard interface for TDM drivers to hook with the framework. 
> - handles various data handling stuff and buffer management.
> 
> In future this Framework will be extended to provide Interface for 
> Line control devices also. For example SLIC, E1/T1 Framers etc.
> 
> Limitations/Future Work
> ---------------------------
> 1. Presently the framework supports only Single Port channelised mode.
> 2. Also the configurability options are limited which will be extended later on.
> 3. Only kernel mode TDM clients are supported currently. Support for 
> User mode clients will be added later.
> 
> Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
> Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
> ---
>  A couple of todos' are left in the patch, we are working on it and 
> will be addressed in the updated patch set.
>  drivers/Kconfig                 |    1 +
>  drivers/Makefile                |    1 +
>  drivers/tdm/Kconfig             |   25 +
>  drivers/tdm/tdm-core.c          | 1146 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mod_devicetable.h |   11 +
>  include/linux/tdm.h             |  347 ++++++++++++
>  6 files changed, 1531 insertions(+), 0 deletions(-)  create mode 
> 100644 drivers/tdm/Kconfig  create mode 100644 drivers/tdm/tdm-core.c  
> create mode 100644 include/linux/tdm.h
> 
> diff --git a/drivers/Kconfig b/drivers/Kconfig index ad6c1eb..25f7f5b 
> 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>  
> +source "drivers/tdm/Kconfig"
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile index 
> cd546eb..362b5ed 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+= infiniband/
>  obj-$(CONFIG_SGI_SN)		+= sn/
>  obj-y				+= firmware/
>  obj-$(CONFIG_CRYPTO)		+= crypto/
> +obj-$(CONFIG_TDM)		+= tdm/
>  obj-$(CONFIG_SUPERH)		+= sh/
>  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
>  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET diff --git 
> a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig new file mode 100644 index 
> 0000000..8db2b05
> --- /dev/null
> +++ b/drivers/tdm/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# TDM subsystem configuration
> +#
> +
> +menuconfig TDM
> +	tristate "TDM support"
> +	---help---
> +	  More information is contained in the directory <file:Documentation/tdm/>,
> +	  especially in the file called "summary" there.
> +	  If you want TDM support, you should say Y here and also to the
> +	  specific driver for your bus adapter(s) below.
> +
> +	  This TDM support can also be built as a module.  If so, the module
> +	  will be called tdm-core.
> +
> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going on.
> +
> +endif # TDM
> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c new file 
> mode 100644 index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program is free software; you can redistribute  it and/or 
> +modify it
> + * under  the terms of  the GNU General  Public License as published 
> +by the
> + * Free Software Foundation;  either version 2 of the  License, or 
> +(at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, 
> +but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License 
> +along
> + * with this program; if not, write  to the Free Software Foundation, 
> +Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"
> +
> +
> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */ 
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */ 
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite 
> +the
> + * new data */
> +static int use_latest_tdm_data = 1;
> +
> +/* this tasklet is created for each adapter instance */ static void 
> +tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */ static int 
> +tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap) 
> +{
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +	adap->drv_count++;
> +
> +	if (!adap->tasklet_conf) {
> +		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
> +						(unsigned long)adap);
> +		adap->tasklet_conf = 1;
> +	}
> +
> +	return TDM_E_OK;
> +}
> +
> +/* Detach client driver and adapter */ static int 
> +tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;
> +
> +	/* If no more driver is registed with the adapter*/
> +	if (!adap->drv_count && adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	if (driver->detach_adapter) {
> +		if (driver->detach_adapter(adap))
> +			pr_err("detach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +
> +	driver->adapter = NULL;
> +	return res;
> +}
> +
> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap) {
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
> +				driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +
> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> + * @adapter: the adapter to add
> + * Context: can sleep
> + *
> + * This routine is used to declare a TDM adapter
> + * When this returns zero, a new device number will be allocated and 
> +stored
> + * in adap->id, and the specified adapter became available for the clients.
> + * Otherwise, a negative errno value is returned.
> + */
> +int tdm_add_adapter(struct tdm_adapter *adapter) {
> +	int id, res = TDM_E_OK;
> +	if (!adapter) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +retry:
> +	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
> +		return -ENOMEM;
> +
> +	mutex_lock(&tdm_core_lock);
> +	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	if (res < 0) {
> +		if (res == -EAGAIN)
> +			goto retry;
> +		return res;
> +	}
> +
> +	adapter->id = id;
> +	return tdm_register_adapter(adapter); } 
> +EXPORT_SYMBOL(tdm_add_adapter);
> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap) {
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	pr_debug("adapter [%s] unregistered\n", adap->name);
> +
> +	list_del(&adap->list);
> +	/* Clear the device structure in case this adapter is ever going to be
> +	   added again */
> +	adap->parent = NULL;
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_del_adapter);
> +
> +/* TDM Client Drivers Registration/De-registration Functions */ int 
> +tdm_register_driver(struct tdm_driver *driver) {
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM 
> +framework
> + * @driver: the driver being unregistered  */ void 
> +tdm_unregister_driver(struct tdm_driver *driver) {
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */
> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name); } 
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}
> +
> +/* We must initialize early, because some subsystems register tdm 
> +drivers
> + * in subsys_initcall() code, but are linked (and initialized) before tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);
> +
> +
> +/* Interface to the tdm device/adapter */
> +
> +/* tdm_adap_send - issue a TDM write
> + * @adap: Handle to TDM device
> + * @buf: Data that will be written to the TDM device
> + * @count: How many bytes to write
> + *
> + * Returns negative errno, or else the number of bytes written.
> + */
> +int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count) {
> +	int res;
> +
> +	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (adap->algo->tdm_write)
> +		res = adap->algo->tdm_write(adap, buf, count);
> +	else {
> +		pr_err("TDM level write not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* If everything went ok (i.e. frame transmitted), return #bytes
> +	   transmitted, else error code. */
> +	return (res == 1) ? count : res;
> +}
> +EXPORT_SYMBOL(tdm_adap_send);
> +
> +/**
> + * tdm_adap_recv - issue a TDM read
> + * @adap: Handle to TDM device
> + * @buf: Where to store data read from TDM device
> + *
> + * Returns negative errno, or else the number of bytes read.
> + */
> +int tdm_adap_recv(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_read)
> +		res = adap->algo->tdm_read(adap, (u16 **)buf);
> +	else {
> +		pr_err("TDM level read not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. frame received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +
> +/**
> + * tdm_adap_get_write_buf - get next write TDM device buffer
> + * @adap: Handle to TDM device
> + * @buf: pointer to TDM device buffer
> + *
> + * Returns negative errno, or else size of the write buffer.
> + */
> +int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_get_write_buf) {
> +		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
> +	} else {
> +		pr_err("TDM level write buf get not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. 1 msg received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_get_write_buf);
> +
> +int tdm_adap_enable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_enable) {
> +		res = adap->algo->tdm_enable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_enable);
> +
> +int tdm_adap_disable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_disable) {
> +		res = adap->algo->tdm_disable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_disable);
> +
> +struct tdm_adapter *tdm_get_adapter(int id) {
> +	struct tdm_adapter *adapter;
> +
> +	mutex_lock(&tdm_core_lock);
> +	adapter = idr_find(&tdm_adapter_idr, id);
> +	if (adapter && !try_module_get(adapter->owner))
> +		adapter = NULL;
> +
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return adapter;
> +}
> +EXPORT_SYMBOL(tdm_get_adapter);
> +
> +void tdm_put_adapter(struct tdm_adapter *adap) {
> +	module_put(adap->owner);
> +}
> +EXPORT_SYMBOL(tdm_put_adapter);
> +
> +
> +/* Port Level APIs of TDM Framework */ unsigned int 
> +tdm_port_open(struct tdm_driver *driver, void **h_port) {
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;
> +
> +	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
> +	port->driver = driver;
> +	port->adapter = adap;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_add_tail(&port->list, &adap->myports);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	INIT_LIST_HEAD(&port->mychannels);
> +
> +	*h_port = port;
> +
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_open);
> +
> +unsigned int tdm_port_close(void *h_port) {
> +	struct tdm_adapter *adap;
> +	struct tdm_driver *driver;
> +	struct tdm_port *port;
> +	struct tdm_channel *temp, *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	driver =  port->driver;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}
> +	adap = driver->adapter;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_del(&port->list);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	if (port->p_port_data != NULL) {
> +		int i;
> +		struct tdm_bd *ch_bd;
> +
> +		/* If the tdm is in channelised mode,
> +		de-allocate the channelised buffer */
> +		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		kfree(port->p_port_data);
> +	}
> +	kfree(port);
> +	return res;
> +out:
> +	if (port)
> +		kfree(port->p_port_data);
> +	kfree(port);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_close);
> +
> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock, flags);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_read);
> +
> +
> +unsigned int tdm_channel_write(void *h_port, void *h_channel,
> +				void *p_data, u16 size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *tx_bd;
> +	unsigned long flags;
> +	int err = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel; #ifdef TDM_CORE_DEBUG
> +	bool data_flag = 0;
> +#endif
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	if (p_data == NULL) { /* invalid data*/
> +		pr_err("Invalid Data");
> +		return -EFAULT;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
> +	tx_bd = channel->p_ch_data->tx_in_data;
> +
> +	if (!tx_bd->flag) {
> +		tx_bd->length = size;
> +		memcpy(tx_bd->p_data, p_data,
> +			size * port->adapter->adapt_cfg.slot_width);
> +		tx_bd->flag = 1;
> +		tx_bd->offset = 0;
> +		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
> +				channel->p_ch_data->tx_data_fifo : tx_bd+1;
> +		port->port_stat.tx_pkt_count++;
> +#ifdef TDM_CORE_DEBUG
> +		data_flag = 1;
> +#endif
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> +						flags);
> +		port->port_stat.tx_pkt_drop_count++;
> +		pr_err("E_NO_MEMORY -Failed Transmit");
> +		return -ENOMEM;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock, flags);
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		int k;
> +		pr_info("\nTX port:%d - Write - Port TX-%d\n",
> +						port->port_id, size);
> +		for (k = 0; k < size; k++)
> +			pr_info("%x", p_data[k]);
> +		pr_info("\n");
> +	}
> +#endif
> +	return err;
> +}
> +EXPORT_SYMBOL(tdm_channel_write);
> +
> +wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port) {
> +	struct tdm_port *port;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return NULL;
> +	}
> +
> +	return &port->ch_wait_queue;
> +
> +}
> +EXPORT_SYMBOL(tdm_port_get_wait_queue);
> +
> +/* Driver Function for select and poll. Based on Port no, it sleeps 
> +on
> + * waitqueue */
> +unsigned int tdm_port_poll(void *h_port, unsigned int wait_time) {
> +	struct tdm_port *port;
> +	unsigned long timeout = msecs_to_jiffies(wait_time);
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	if (!port->p_port_data || !port->in_use)
> +		return -EIO;
> +
> +	if (port->p_port_data->rx_out_data->flag) {
> +		pr_debug("Data Available");
> +		return TDM_E_OK;
> +	}
> +	if (timeout) {
> +		wait_event_interruptible_timeout(port->ch_wait_queue,
> +					  port->p_port_data->rx_out_data->flag,
> +					  timeout);
> +
> +		if (port->p_port_data->rx_out_data->flag) {
> +			pr_debug("Data Available");
> +			return TDM_E_OK;
> +		}
> +	}
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(tdm_port_poll);
> +
> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats 
> +*portStat) {
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}
> +
> +		if (ch_data) {
> +			/*	Wake up the Port Data Poll event */
> +			wake_up_interruptible(&port->ch_wait_queue);
> +#ifdef	TDM_CORE_DEBUG
> +			pr_info("Port RX-%d-%d\n", channel->ch_id, ch_data_len);
> +			for (i = 0; i < ch_data_len; i++)
> +				pr_info("%x", pcm_buffer[i]);
> +			pr_info("\n");
> +#endif
> +			port->port_stat.rx_pkt_count++;
> +			ch_data = 0;
> +		}
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_data_tx_interleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	bool last_data = 0;
> +	u16 *output_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +#ifdef TDM_CORE_DEBUG
> +	u8	data_flag = 0;
> +#endif
> +
> +	if (adap == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	buf_size = tdm_adap_get_write_buf(adap, (void **)&output_tdm_buffer);
> +	if (buf_size <= 0 || !output_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = 
> +bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
> +
> +
> +	memset(output_tdm_buffer, 0, sizeof(buf_size));
> +
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* check if the port is open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +								list) {
> +		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
> +
> +
> +		/* if the channel is open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +
> +		spin_lock(&channel->p_ch_data->tx_channel_lock);
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +			ch_bd = channel->p_ch_data->tx_out_data;
> +			if (ch_bd->flag) {
> +				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
> +						ch_bd->offset);
> +				/*if the buffer has less frames than required */
> +				if (frame_ch_data_size >=
> +					((ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width))) {
> +					ch_data_len =
> +					(ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width);
> +					last_data = 1;
> +				} else {
> +					ch_data_len = frame_ch_data_size;
> +				}
> +				/* Interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					/* TODO- need to be generic for any size
> +					   assignment*/
> +					output_tdm_buffer[channel->ch_id +
> +						bytes_slot_offset * i] =
> +								pcm_buffer[i];
> +				}
> +				/* If all the data of this buffer is
> +							transmitted */
> +				if (last_data) {
> +					ch_bd->flag = 0;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->tx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->tx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.tx_pkt_conf_count++;
> +				} else {
> +					ch_bd->offset += ch_data_len *
> +						(adap->adapt_cfg.slot_width);
> +				}
> +#ifdef	TDM_CORE_DEBUG
> +				data_flag = 1;
> +#endif
> +			}
> +		spin_unlock(&channel->p_ch_data->tx_channel_lock);
> +		}
> +	}
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		pr_info("TX-TDM Interleaved Data-\n");
> +		for (i = 0; i < 64; i++)
> +			pr_info("%x", output_tdm_buffer[i]);
> +		pr_info("\n");
> +	  }
> +#endif
> +	return TDM_E_OK;
> +}
> +
> +/* Channel Level APIs of TDM Framework */ int tdm_channel_open(u16 
> +chanid, u16 ch_width, struct tdm_port *port,
> +				void **h_channel)
> +{
> +	struct tdm_channel *channel, *temp;
> +	unsigned long		flags;
> +	struct tdm_ch_data	*p_ch_data;
> +	int res = TDM_E_OK;
> +
> +	if (!(port && h_channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +		if (channel->ch_id == chanid) {
> +			pr_err("%s: Channel %d already open\n",
> +						__func__, chanid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> +	if (!channel) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
> +	if (!p_ch_data) {
> +		res = -ENOMEM;
> +		goto outdata;
> +	}
> +
> +	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
> +	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
> +
> +	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
> +	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
> +	spin_lock_init(&p_ch_data->rx_channel_lock);
> +	spin_lock_init(&p_ch_data->tx_channel_lock);
> +
> +	channel->p_ch_data = p_ch_data;
> +
> +	channel->ch_id = chanid;
> +	channel->ch_cfg.first_slot = chanid;
> +	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized mode and
> +						configurable for other modes */
> +	channel->port = port;
> +	channel->in_use = 1;
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_add_tail(&channel->list, &port->mychannels);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +	*h_channel = channel;
> +
> +	return res;
> +
> +outdata:
> +	kfree(channel);
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_open);
> +
> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_del(&channel->list);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +out:
> +	if (channel)
> +		kfree(channel->p_ch_data);
> +	kfree(channel);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_close);
> +
> +void init_config_adapter(struct tdm_adapter *adap) {
> +	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
> +		.loopback = e_TDM_PROCESS_NORMAL,
> +		.num_ch = NUM_CHANNELS,
> +		.ch_size_type = CHANNEL_16BIT_LIN,
> +		.frame_len = NUM_SAMPLES_PER_FRAME,
> +		.num_frames = NUM_SAMPLES_PER_FRAME,
> +		.adap_mode = e_TDM_ADAPTER_MODE_NONE
> +			 };
> +
> +	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 + 1;
> +
> +	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
> +		sizeof(struct fsl_tdm_adapt_cfg));
> +
> +	return;
> +}
> +EXPORT_SYMBOL(init_config_adapter);
> +
> +static void tdm_data_tasklet_fn(unsigned long data) {
> +	struct tdm_adapter *adapter;
> +	adapter = (struct tdm_adapter *)data;
> +	if (adapter != NULL) {
> +		tdm_data_tx_interleave(adapter);
> +		tdm_data_rx_deinterleave(adapter);
> +	}
> +}
> +
> +
> +MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
> +	"Rajesh Gumasta <rajesh.gumasta@freescale.com>"); 
> +MODULE_DESCRIPTION("TDM Driver Framework Core"); 
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mod_devicetable.h 
> b/include/linux/mod_devicetable.h index ae28e93..dc1a655 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -416,6 +416,17 @@ struct i2c_device_id {
>  			__attribute__((aligned(sizeof(kernel_ulong_t))));
>  };
>  
> +/* tdm */
> +
> +#define TDM_NAME_SIZE   20
> +#define TDM_MODULE_PREFIX "tdm:"
> +
> +struct tdm_device_id {
> +	char name[TDM_NAME_SIZE];
> +	kernel_ulong_t driver_data      /* Data private to the driver */
> +			__attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
> +
>  /* spi */
>  
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/tdm.h b/include/linux/tdm.h new file mode 
> 100644 index 0000000..8cf4ef5
> --- /dev/null
> +++ b/include/linux/tdm.h
> @@ -0,0 +1,347 @@
> +/* include/linux/tdm.h
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * tdm.h - definitions for the tdm-device framework interface
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * This program is free software; you can redistribute  it and/or 
> +modify it
> + * under  the terms of  the GNU General  Public License as published 
> +by the
> + * Free Software Foundation;  either version 2 of the  License, or 
> +(at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, 
> +but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the  GNU General Public License 
> +along
> + * with this program; if not, write  to the Free Software Foundation, 
> +Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3
> +
> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment 
> +*/ static inline int ALIGN_SIZE(u64 size, u32 alignment) {
> +	return (size + alignment - 1) & (~(alignment - 1)); }
> +
> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)  */ struct 
> +tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);
> +
> +	const struct tdm_device_id *id_table;
> +
> +	/* The associated adapter for this driver */
> +	struct tdm_adapter *adapter;
> +	struct list_head list;
> +};
> +
> +/* tdm per port statistics structure, used for providing and storing 
> +tdm port
> + * statistics.
> + */
> +struct tdm_port_stats {
> +	unsigned int rx_pkt_count;	/* Rx frame count per channel */
> +	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
> +					 clean space for new buffer */
> +	unsigned int tx_pkt_count;	/* Tx frame count per channel */
> +	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count per
> +					 channel */
> +	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel due to
> +					 queue full */
> +};
> +
> +
> +/* tdm Buffer Descriptor, used for Creating Interleaved and 
> +De-interleaved
> + * FIFOs
> + */
> +struct tdm_bd {
> +	unsigned char flag;		/* BD is full or empty */
> +	unsigned char wrap;		/* BD is last in the queue */
> +	unsigned short length;	/* Length of Data in BD */
> +	/*TODO: use dyanmic memory */
> +	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
> +	unsigned long offset;	/* Offset of the Data Pointer to be used */
> +};
> +
> +#define TDM_CH_RX_BD_RING_SIZE	3
> +#define TDM_CH_TX_BD_RING_SIZE	3
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_port_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel Data
> +								BD Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
> +							read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel Data
> +								BD Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
> +						 filled by App */
> +	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
> +};
> +
> +/* structure tdm_port_cfg - contains configuration params for a port 
> +*/ struct tdm_port_cfg {
> +	unsigned short port_mode;
> +};
> +
> +/* struct tdm_port - represent an TDM ports for a device */ struct 
> +tdm_port {
> +	unsigned short port_id;
> +	unsigned short in_use;		/* Port is enabled? */
> +	uint16_t rx_max_frames;		/* Received Port frames
> +					 before allowing Read Operation in
> +					 Port Mode */
> +
> +	struct tdm_port_stats port_stat;/* A structure parameters defining
> +					 TDM port statistics. */
> +	struct tdm_port_data *p_port_data;	/* a structure parameters
> +						defining tdm channelised data */
> +	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data */
> +
> +	struct tdm_driver *driver;	/* driver for this port */
> +	struct tdm_adapter *adapter;	/* adapter for this port */
> +	struct list_head list;		/* list of ports */
> +	struct list_head mychannels;	/* list of channels, created on this
> +					 port*/
> +	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
> +	struct tdm_port_cfg port_cfg;/* A structure parameters defining
> +					 TDM port configuration. */
> +};
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_ch_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data BD
> +								Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data BD
> +								Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
> +						App */
> +	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
> +};
> +
> +/* Channel config params */
> +struct tdm_ch_cfg {
> +	unsigned short num_slots;
> +	unsigned short first_slot;
> +};
> +
> +/* struct tdm_channel- represent a TDM channel for a port */ struct 
> +tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
> +};
> +
> +/* tdm_adapt_algorithm is for accessing the routines of device */ 
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *); };
> +
> +/* tdm_adapter_mode is to define in mode of the device */ enum 
> +tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to 
> +operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or 
> +internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};
> +
> +
> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */
> +	u32 num_frames;
> +	u8 loopback;			/* loopback or normal */
> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */
> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */
> +	int max_timeslots;		/* Max Number of timeslots that are
> +					supported on this adapter */
> +};
> +
> +/*
> + * tdm_adapter is the structure used to identify a physical tdm 
> +device along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */
> +	unsigned int class;	/* classes to allow probing for */
> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/
> +
> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;
> +
> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;
> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> +	void *data;
> +	struct fsl_tdm_adapt_cfg adapt_cfg;
> +};
> +
> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev) {
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void 
> +*data) {
> +	dev->data = data;
> +}
> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *); extern int 
> +tdm_del_adapter(struct tdm_adapter *); extern int 
> +tdm_register_driver(struct tdm_driver *); extern void 
> +tdm_del_driver(struct tdm_driver *); extern void 
> +tdm_unregister_driver(struct tdm_driver *); extern void 
> +init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **); 
> +extern unsigned int tdm_port_close(void *); extern unsigned int 
> +tdm_port_ioctl(void *, unsigned int, unsigned long); extern unsigned 
> +int tdm_channel_read(void *, void *, void *, u16 *); extern unsigned 
> +int tdm_channel_write(void *, void * , void *, u16); extern unsigned 
> +int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **); 
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);
> +
> +static inline int tdm_add_driver(struct tdm_driver *driver) {
> +	return tdm_register_driver(driver);
> +}
> +
> +extern struct tdm_adapter *tdm_get_adapter(int id); extern void 
> +tdm_put_adapter(struct tdm_adapter *adap);
> +
> +#endif /* __KERNEL__ */
> +
> +#define TDM_E_OK 0
> +
> +#endif /* _LINUX_TDM_H */
Aggrwal Poonam-B10812 April 27, 2012, 2:07 a.m. UTC | #5
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Tuesday, April 24, 2012 6:05 AM
> To: Aggrwal Poonam-B10812
> Cc: linuxppc-dev@lists.ozlabs.org; Singh Sandeep-B37400
> Subject: Re: [PATCH][2/3][RFC] TDM Framework
> 
Thanks Scott for the comments, we will incorporate them and send the next version.

Regards
Poonam
> On 03/10/2012 06:57 AM, Poonam Aggrwal wrote:
> > diff --git a/drivers/Kconfig b/drivers/Kconfig index ad6c1eb..25f7f5b
> > 100644
> > --- a/drivers/Kconfig
> > +++ b/drivers/Kconfig
> > @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
> >
> >  source "drivers/net/dpa/NetCommSw/Kconfig"
> >
> > +source "drivers/tdm/Kconfig"
> >  endmenu
> 
> When posting patches to this list, please ensure that they are based on
> an upstream Linux kernel and not a Freescale BSP kernel.
Sure, but this was RFC patch set where the primary intention was to get design level comments.
But we will send the updated patches rebased on upstream head.
> 
> > +if TDM
> > +
> > +config TDM_DEBUG_CORE
> > +	bool "TDM Core debugging messages"
> > +	help
> > +	  Say Y here if you want the TDM core to produce a bunch of debug
> > +	  messages to the system log.  Select this if you are having a
> > +	  problem with TDM support and want to see more of what is going
> on.
> > +
> > +endif # TDM
> 
> Please use the normal kernel mechanisms for controlling debug messages.
> 
okay
> > diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c new file
> > mode 100644 index 0000000..cdda260
> > --- /dev/null
> > +++ b/drivers/tdm/tdm-core.c
> > @@ -0,0 +1,1146 @@
> > +/* driver/tdm/tdm-core.c
> > + *
> > + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights
> reserved.
> > + *
> > + * TDM core is the interface between TDM clients and TDM devices.
> > + * It is also intended to serve as an interface for line controld
> > + * devices later on.
> > + *
> > + * Author:Hemant Agrawal <hemant@freescale.com>
> > + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> > + *
> > + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> > + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> > + * 1. Added framework based initialization of device.
> > + * 2. All the init/run time configuration is now done by framework.
> > + * 3. Added channel level operations.
> > + *
> > + * This program is free software; you can redistribute  it and/or
> > +modify it
> > + * under  the terms of  the GNU General  Public License as published
> > +by the
> > + * Free Software Foundation;  either version 2 of the  License, or
> > +(at your
> > + * option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > +but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + *
> > + * You should have received a copy of the  GNU General Public License
> > +along
> > + * with this program; if not, write  to the Free Software Foundation,
> > +Inc.,
> > + * 675 Mass Ave, Cambridge, MA 02139, USA.
> > + */
> > +
> > +/* if read write debug required */
> > +#undef TDM_CORE_DEBUG
> > +
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/slab.h>
> > +#include <linux/tdm.h>
> > +#include <linux/init.h>
> > +#include <linux/idr.h>
> > +#include <linux/mutex.h>
> > +#include <linux/completion.h>
> > +#include <linux/hardirq.h>
> > +#include <linux/irqflags.h>
> > +#include <linux/list.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/io.h>
> > +#include "device/tdm_fsl.h"
> 
> What is a reference to tdm_fsl.h doing in the presumably hardware-
> independent tdm-core.c?
Need to check that, ideally this include should not be required
> 
> > +static DEFINE_MUTEX(tdm_core_lock);
> > +static DEFINE_IDR(tdm_adapter_idr);
> > +/* List of TDM adapters registered with TDM framework */
> > +LIST_HEAD(adapter_list);
> > +
> > +/* List of TDM clients registered with TDM framework */
> > +LIST_HEAD(driver_list);
> > +
> > +/* In case the previous data is not fetched by the client driver, the
> > + * de-interleaving function will  discard the old data and rewrite
> > +the
> > + * new data */
> > +static int use_latest_tdm_data = 1;
> 
> Describe when one would want to change this, and provide a better way to
> change it (e.g. module parameter or sysfs knob).
> 
Ok, description can be added.
Will also explore the sysfs knob option.                                                                                                                                                                                           
> /*
>  * Linux kernel
>  * multi-line comment style
>  * is like this.
>  */
> 
Sure
> > +/* this tasklet is created for each adapter instance */ static void
> > +tdm_data_tasklet_fn(unsigned long);
> > +
> > +/* tries to match client driver with the adapter */ static int
> > +tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> > +{
> > +	/* match on an id table if there is one */
> > +	if (driver->id_table && driver->id_table->name[0]) {
> > +		if (!(strcmp(driver->id_table->name, adap->name)))
> > +			return (int)driver->id_table;
> > +	}
> > +	return TDM_E_OK;
> > +}
> 
> s/TDM_E_OK/0/g
> 
ok
> > +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> > +					struct tdm_adapter *adap)
> > +{
> > +	/* if driver is already attached to any other adapter, return*/
> > +	if (driver->adapter && (driver->adapter != adap))
> > +		return TDM_E_OK;
> 
> Why can't one driver service multiple adapters?  How would multiple
> drivers service one adapter?  Are there sub-devices that need individual
> controlling?
> 
Driver is here client driver which would be using 
> > +	driver->adapter = adap;
> > +
> > +	if (driver->attach_adapter) {
> > +		if (driver->attach_adapter(adap) < 0)
> > +			/* We ignore the return code; if it fails, too bad */
> > +			pr_err("attach_adapter failed for driver [%s]\n",
> > +				driver->name);
> 
> Why ignore errors?
This will be fixed.

> 
> > +/* Detach client driver and adapter */ static int
> > +tdm_detach_driver_adap(struct tdm_driver *driver,
> > +					struct tdm_adapter *adap)
> > +{
> > +	int res = TDM_E_OK;
> > +
> > +	if (!driver->adapter || (driver->adapter != adap))
> > +		return TDM_E_OK;
> 
> Shouldn't this be an error?
Will check this.

> 
> > +	if (!driver->detach_adapter)
> > +		return TDM_E_OK;
> > +
> > +	adap->drv_count--;
> 
> If the driver doesn't have a detach_adapter method, you skip decrementing
> the count and leave the tasklet lying around?
> 
This has been fixed in later version of the patch which we made for FSL BSP.
Will fix this here also.
> > +/* TDM adapter Registration/De-registration with TDM framework */
> > +
> > +static int tdm_register_adapter(struct tdm_adapter *adap) {
> > +	int res = TDM_E_OK;
> > +	struct tdm_driver *driver, *next;
> > +
> > +	if (!adap) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return -EINVAL;
> > +	}
> 
> Pointers aren't handles.  The caller should not be passing NULL, and it
> would be more useful to crash and get a backtrace if it does.  It's not
> realistic to check every pointer for being NULL when there's no
> legitimate reason it could be NULL, and it doesn't help you if you have
> some other bad value besides NULL.
Okay, we will check this.
> 
> > +	mutex_init(&adap->adap_lock);
> > +	INIT_LIST_HEAD(&adap->myports);
> > +	spin_lock_init(&adap->portlist_lock);
> > +
> > +	adap->drv_count = 0;
> > +	adap->tasklet_conf = 0;
> > +
> > +	list_add_tail(&adap->list, &adapter_list);
> > +
> > +	/* initialization of driver by framework in default configuration
> */
> > +	init_config_adapter(adap);
> > +
> > +	/* Notify drivers */
> > +	pr_info("adapter [%s] registered\n", adap->name);
> 
> This is too noisy.  You haven't even gotten a match yet.
> 
It is telling that these adapters have registered to the framework.
> > +/*
> > + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> 
> Why are there device numbers at all?
> 
> I suspect there's a fair bit of copy and paste going on of another
> subsystem's quirks (i2c?).  I don't see any mention in the copyright
> block of this code having been derived from anything else, though...
> 
When we initially started, we took i2c as an example. But later not much could be used as-is.
But yes some initial stuff is from i2c. Will update it in copyright section.
> > +
> > +
> > +/**
> > + * tdm_del_adapter - unregister TDM adapter
> > + * @adap: the adapter being unregistered
> > + *
> > + * This unregisters an TDM adapter which was previously registered
> > + * by @tdm_add_adapter.
> > + */
> > +int tdm_del_adapter(struct tdm_adapter *adap) {
> > +	int res = TDM_E_OK;
> > +	struct tdm_adapter *found;
> > +	struct tdm_driver *driver, *next;
> > +
> > +	if (!adap) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* First make sure that this adapter was ever added */
> > +	mutex_lock(&tdm_core_lock);
> > +	found = idr_find(&tdm_adapter_idr, adap->id);
> > +	mutex_unlock(&tdm_core_lock);
> > +	if (found != adap) {
> > +		pr_err("tdm-core: attempting to delete unregistered "
> > +			 "adapter [%s]\n", adap->name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*disable and kill the data processing tasklet */
> > +	if (adap->tasklet_conf) {
> > +		tasklet_disable(&adap->tdm_data_tasklet);
> > +		tasklet_kill(&adap->tdm_data_tasklet);
> > +		adap->tasklet_conf = 0;
> > +	}
> > +
> > +	/* Detach any active ports. This can't fail, thus we do not
> > +	   checking the returned value. */
> > +	mutex_lock(&tdm_core_lock);
> > +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> > +		if (tdm_device_match(driver, adap)) {
> > +			tdm_detach_driver_adap(driver, adap);
> > +			pr_info(
> > +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> > +				 driver->id, adap->name, adap->id);
> > +		}
> > +	}
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	mutex_lock(&tdm_core_lock);
> > +	idr_remove(&tdm_adapter_idr, adap->id);
> > +	mutex_unlock(&tdm_core_lock);
> 
> Why are you dropping the lock then reacquiring it?
> 
Mistake, will fix it.
> > +/* TDM Client Drivers Registration/De-registration Functions */ int
> > +tdm_register_driver(struct tdm_driver *driver) {
> > +	int res = TDM_E_OK;
> > +	struct tdm_adapter *adap, *next;
> > +
> > +	list_add_tail(&driver->list, &driver_list);
> > +
> > +	mutex_lock(&tdm_core_lock);
> > +	/* Walk the adapters that are already present */
> > +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> > +		if (tdm_device_match(driver, adap)) {
> > +			res = tdm_attach_driver_adap(driver, adap);
> > +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> > +				"%s(ID = %d) drv_count=%d", driver->id,
> > +				adap->name, adap->id, adap->drv_count);
> > +		break;
> > +		}
> > +	}
> 
> Indentation.
> 
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	return res;
> > +}
> > +EXPORT_SYMBOL(tdm_register_driver);
> > +
> > +/*
> > + * tdm_unregister_driver - unregister TDM client driver from TDM
> > +framework
> > + * @driver: the driver being unregistered  */ void
> > +tdm_unregister_driver(struct tdm_driver *driver) {
> > +	if (!driver) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return;
> > +	}
> > +       /* A driver can register to only one adapter,
> > +	* so no need to browse the list */
> 
> Whitespace.
> 
> > +	mutex_lock(&tdm_core_lock);
> > +	tdm_detach_driver_adap(driver, driver->adapter);
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	list_del(&driver->list);
> > +
> > +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name); }
> > +EXPORT_SYMBOL(tdm_unregister_driver);
> > +
> > +/* TDM Framework init and exit */
> > +static int __init tdm_init(void)
> > +{
> > +	pr_info("%s\n", __func__);
> > +	return TDM_E_OK;
> > +}
> > +
> > +static void __exit tdm_exit(void)
> > +{
> > +	pr_info("%s\n", __func__);
> > +	return;
> > +}
> 
> Don't spam the console just because the driver got loaded or unloaded (at
> this point you haven't even found the hardware).
> 
Ok

> > +/* We must initialize early, because some subsystems register tdm
> > +drivers
> > + * in subsys_initcall() code, but are linked (and initialized) before
> tdm.
> > + */
> > +postcore_initcall(tdm_init);
> > +module_exit(tdm_exit);
> 
> tdm_init doesn't do anything, so why does it need to run early?
> 
> > +/* Port Level APIs of TDM Framework */ unsigned int
> > +tdm_port_open(struct tdm_driver *driver, void **h_port)
> 
> Why is the return unsigned int?  You're returning negative numbers.
> 
> Also consider having the return be a pointer, and use PTR_ERR/ERR_PTR --
> or at least put a proper type on h_port (what is the "h_"?).
> 
> > +{
> > +	struct tdm_port *port;
> > +	struct tdm_adapter *adap;
> > +	unsigned long		flags;
> > +	int res = TDM_E_OK;
> > +
> > +	if (driver == NULL) {
> > +		pr_err("driver NULL\n");
> > +		return -ENODEV;
> > +	}
> > +	if (driver->adapter == NULL) {
> > +		pr_err("adapter NULL\n");
> > +		return -ENODEV;
> > +	}
> 
> Either make these pr_debug (or remove them), or make the message more
> specific.
> 
> > +	adap = tdm_get_adapter(driver->adapter->id);
> > +	if (!adap)
> > +		return -ENODEV;
> > +
> > +	/* This creates an anonymous tdm_port, which may later be
> > +	 * pointed to some slot.
> > +	 *
> > +	 */
> > +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> > +	if (!port) {
> > +		res = -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	init_waitqueue_head(&port->ch_wait_queue);
> > +
> > +
> > +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> > +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> > +
> > +	port->in_use = 1;
> 
> When would a port have in_use be false, other than this brief window
> where nothing else should be looking at it?
> 
> > +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> > +	if (channel)
> > +		if (channel->in_use) {
> > +			pr_err("%s: Cannot close port. Channel in use\n",
> > +								__func__);
> > +			res = -ENXIO;
> > +			goto out;
> > +			}
> > +	}
> 
> Broken indentation.
> 
> > +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> > +				void *p_data, u16 *size)
> > +{
> > +	struct tdm_port *port;
> > +	struct tdm_channel *channel;
> > +	struct tdm_bd *rx_bd;
> > +	unsigned long flags;
> > +	int i, res = TDM_E_OK;
> > +	unsigned short *buf, *buf1;
> > +	port = (struct tdm_port *)h_port;
> > +	channel = (struct tdm_channel *)h_channel;
> 
> Unnecessary casts.
> 
> > +	if ((port && channel) == 0) { /* invalid handle*/
> 
> This is an odd construct -- how about "if (!port || !channel)"?
> 
> > +		pr_err("%s:Invalid Handle\n", __func__);
> > +		return -ENXIO;
> > +	}
> > +
> > +	if (!port->in_use)
> > +		return -EIO;
> > +	if (!channel->p_ch_data || !channel->in_use)
> > +		return -EIO;
> > +
> > +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> 
> Shouldn't you test whether it's in use after you get the lock?
> 
> > +	rx_bd = channel->p_ch_data->rx_out_data;
> > +
> > +	if (rx_bd->flag) {
> > +		*size = rx_bd->length;
> > +		buf = (u16 *) p_data;
> > +		buf1 = (u16 *)rx_bd->p_data;
> > +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> > +			buf[i] = buf1[i];
> > +		rx_bd->flag = 0;
> > +		rx_bd->offset = 0;
> > +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> > +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> > +
> > +	} else {
> > +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> > +						flags);
> > +		pr_info("No Data Available");
> > +		return -EAGAIN;
> > +	}
> 
> That pr_info() is inappropriate.  This driver appears to be overly chatty
> in general (and with quite vague messages) -- or is this debug stuff that
> will be removed in a non-RFC patch?
> 
> > +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats
> > +*portStat) {
> > +	struct tdm_port *port;
> > +	int port_num;
> > +	port = (struct tdm_port *)h_port;
> > +
> > +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> > +		pr_err("Invalid Handle");
> > +		return -ENXIO;
> > +	}
> > +	port_num =  port->port_id;
> > +
> > +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> > +
> > +	pr_info("TDM Port %d Get Stats", port_num);
> > +
> > +	return TDM_E_OK;
> > +}
> > +EXPORT_SYMBOL(tdm_port_get_stats);
> > +
> > +/* Data handling functions */
> > +
> > +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap) {
> > +	struct tdm_port *port, *next;
> > +	struct tdm_channel *channel, *temp;
> > +	struct tdm_bd	*ch_bd;
> > +
> > +	int i, buf_size, ch_data_len;
> > +	u16 *input_tdm_buffer;
> > +	u16 *pcm_buffer;
> > +	int slot_width;
> > +	int frame_ch_data_size;
> > +	bool ch_data;
> > +	int bytes_in_fifo_per_frame;
> > +	int bytes_slot_offset;
> > +
> > +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> > +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> > +	ch_data = 0;
> > +
> > +	if (!adap) { /* invalid handle*/
> > +		pr_err("%s: Invalid Handle\n", __func__);
> > +		return -ENXIO;
> > +	}
> > +
> > +	slot_width = adap->adapt_cfg.slot_width;
> > +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> > +	if (buf_size <= 0 || !input_tdm_buffer)
> > +		return -EINVAL;
> > +
> > +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> > +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> > +
> > +	/* de-interleaving for all ports*/
> > +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> > +
> > +		/* if the port is not open */
> > +		if (!port->in_use)
> > +			continue;
> > +
> > +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> > +							list) {
> > +		/* if the channel is not open */
> > +		if (!channel->in_use || !channel->p_ch_data)
> > +			continue;
> > +		ch_bd = channel->p_ch_data->rx_in_data;
> > +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> > +			/*if old data is to be discarded */
> > +		if (use_latest_tdm_data)
> > +			if (ch_bd->flag) {
> > +				ch_bd->flag = 0;
> > +				ch_bd->offset = 0;
> > +				if (ch_bd == channel->p_ch_data->rx_out_data)
> > +					channel->p_ch_data->rx_out_data =
> > +						ch_bd->wrap ?
> > +						channel->p_ch_data->rx_data_fifo
> > +						: ch_bd+1;
> > +					port->port_stat.rx_pkt_drop_count++;
> > +				}
> > +			/* if the bd is empty */
> > +			if (!ch_bd->flag) {
> > +				if (ch_bd->offset == 0)
> > +					ch_bd->length = port->rx_max_frames;
> > +
> > +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> > +				/* De-interleaving the data */
> > +				for (i = 0; i < ch_data_len; i++) {
> > +					pcm_buffer[i]
> > +					= input_tdm_buffer[i*bytes_slot_offset +
> > +						channel->ch_id];
> > +				}
> > +				ch_bd->offset += ch_data_len * slot_width;
> > +
> > +				if (ch_bd->offset >=
> > +					(ch_bd->length - frame_ch_data_size)*
> > +						(adap->adapt_cfg.slot_width)) {
> > +					ch_bd->flag = 1;
> > +					ch_bd->offset = 0;
> > +					channel->p_ch_data->rx_in_data =
> > +						ch_bd->wrap ?
> > +						channel->p_ch_data->rx_data_fifo
> > +						: ch_bd+1;
> > +					ch_data = 1;
> > +				}
> > +			} else {
> > +				port->port_stat.rx_pkt_drop_count++;
> > +			}
> > +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> > +		}
> 
> Broken indentation.  Spaces around operators.
> 
> > +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> > +				struct tdm_channel *h_channel)
> > +{
> > +	struct tdm_channel *channel;
> > +	unsigned long		flags;
> > +	int res = TDM_E_OK;
> > +	channel = h_channel;
> > +
> > +	if (!(port && channel)) {
> > +		pr_err("%s: Invalid handle\n", __func__);
> > +		res = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	if (ch_width != 1) {
> > +		pr_err("%s: Mode not supported\n", __func__);
> > +		res = -EINVAL;
> > +		goto out;
> > +	}
> 
> close() seems an odd time to be checking for supported channel width,
> especially since you don't use that variable anywhere else in this
> function.
> 
> > +#ifndef _LINUX_TDM_H
> > +#define _LINUX_TDM_H
> > +
> > +#ifdef __KERNEL__
> 
> Is this supposed to be a userspace header ever?  If TDM exposes a
> userspace interface, it needs to be documented.
> 
> > +#include <linux/types.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/device.h>	/* for struct device */
> > +#include <linux/sched.h>	/* for completion */
> > +#include <linux/mutex.h>
> > +#include <linux/interrupt.h>
> > +
> > +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> > +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> > +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> > +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> > +
> > +#define NUM_CHANNELS		16
> > +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> > +						 channel. Req for voice data */
> > +#define NUM_MS			10
> > +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /*
> Number of
> > +						samples for 1 client buffer */
> > +#define NUM_OF_TDM_BUF		3
> 
> These need proper namespacing -- plus, should all of these really be
> hardcoded like this?  Is this standard stuff that will always be the
> same?
> 
> > +/* General options */
> > +
> > +struct tdm_adapt_algorithm;
> > +struct tdm_adapter;
> > +struct tdm_port;
> > +struct tdm_driver;
> > +
> > +/* Align addr on a size boundary - adjust address up if needed */
> > +/* returns min value greater than size which is multiple of alignment
> > +*/ static inline int ALIGN_SIZE(u64 size, u32 alignment) {
> > +	return (size + alignment - 1) & (~(alignment - 1)); }
> 
> Use the already existing ALIGN().
> 
> > +/**
> > + * struct tdm_driver - represent an TDM device driver
> > + * @class: What kind of tdm device we instantiate (for detect)
> > + * @id:Driver id
> > + * @name: Name of the driver
> > + * @attach_adapter: Callback for device addition (for legacy drivers)
> > + * @detach_adapter: Callback for device removal (for legacy drivers)
> > + * @probe: Callback for device binding
> > + * @remove: Callback for device unbinding
> > + * @shutdown: Callback for device shutdown
> > + * @suspend: Callback for device suspend
> > + * @resume: Callback for device resume
> > + * @command: Callback for sending commands to device
> > + * @id_table: List of TDM devices supported by this driver
> > + * @list: List of drivers created (for tdm-core use only)  */ struct
> > +tdm_driver {
> > +	unsigned int class;
> > +	unsigned int id;
> > +	char name[TDM_NAME_SIZE];
> > +
> > +	int (*attach_adapter)(struct tdm_adapter *);
> > +	int (*detach_adapter)(struct tdm_adapter *);
> > +
> > +	/* Standard driver model interfaces */
> > +	int (*probe)(const struct tdm_device_id *);
> > +	int (*remove)(void);
> > +
> > +	/* driver model interfaces that don't relate to enumeration */
> > +	void (*shutdown)(void);
> > +	int (*suspend)(pm_message_t mesg);
> > +	int (*resume)(void);
> > +
> > +	/* a ioctl like command that can be used to perform specific
> functions
> > +	 * with the device.
> > +	 */
> > +	int (*command)(unsigned int cmd, void *arg);
> 
> Please describe what semantics you expect for the non-standard functions.
> 
> Where are the "ioctl like commands" defined?  When would they be used?
> I don't see it used in this patchset.
> 
> > +/* struct tdm_channel- represent a TDM channel for a port */ struct
> > +tdm_channel {
> > +	u16 ch_id;			/* logical channel number */
> > +	struct list_head list;		/* list of channels in a port*/
> > +	struct tdm_port *port;		/* port for this channel */
> > +	u16 in_use;			/* channel is enabled? */
> > +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> > +	struct tdm_ch_data *p_ch_data;	/* data storage space for
> channel */
> > +};
> 
> Why are ch_id and especially in_use u16?
> 
> > +/* tdm_adapt_algorithm is for accessing the routines of device */
> > +struct tdm_adapt_algorithm {
> > +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> > +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> > +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> > +	int (*tdm_enable)(struct tdm_adapter *);
> > +	int (*tdm_disable)(struct tdm_adapter *); };
> >
> 
> Provide parameter names and document the semantics you're expecting.
> 
> > +/* tdm_adapter_mode is to define in mode of the device */ enum
> > +tdm_adapter_mode {
> > +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> > +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> > +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> > +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> > +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> > +};
> > +
> > +/* tdm_port_mode defines the mode in which the port is configured to
> > +operate
> > + * It can be channelized/full/fractional.
> > + */
> > +enum tdm_port_mode {
> > +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> > +	, e_TDM_PORT_FULL = 1		/* Full mode */
> > +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> > +};
> > +
> > +/* tdm_process_mode used for testing the tdm device in normal mode or
> > +internal
> > + * loopback or external loopback
> > + */
> > +enum tdm_process_mode {
> > +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> > +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> > +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> > +};
> 
> Commas go at the end of lines, not the beginning.
> 
> No hungarian notation -- drop the "e_".
> 
> > +/* TDM configuration parameters */
> > +struct fsl_tdm_adapt_cfg {
> > +	u8 num_ch;		/* Number of channels in this adpater */
> > +	u8 ch_size_type;		/* reciever/transmit channel
> > +						size for all channels */
> > +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> > +	u8 frame_len;		/* Length of frame in samples */
> 
> Is u8 really appropriate here?  Maybe int?
> 
> What does "type" mean in "ch_size_type"?
> 
> > +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> > +						4 = E1-FULL */
> 
> Use #defines or an enum.
> 
> > +	int max_num_ports;		/* Not Used: Max Number of ports that
> > +					can be created on this adapter */
> 
> If it's not used, why is it here?
> 
> > +/*
> > + * tdm_adapter is the structure used to identify a physical tdm
> > +device along
> > + * with the access algorithms necessary to access it.
> > + */
> > +struct tdm_adapter {
> > +	struct module *owner;	/* owner of the adapter module */
> > +	unsigned int id;	/* Adapter Id */
> 
> What does the id mean?  Why do we need an arbitrary numberspace?
> 
> > +	unsigned int class;	/* classes to allow probing for */
> 
> What sort of values would go here?
> 
> > +	unsigned int drv_count;	/* Number of drivers associated with the
> > +				 adapter */
> > +
> > +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to
> access the
> > +						 adapter*/
> > +
> > +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> > +	struct mutex adap_lock;
> > +	struct device *parent;		/*Not Used*/
> 
> Why is the parent device not used?
> 
> > +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to
> perform
> > +						 data processing*/
> > +	int tasklet_conf;	/* flag for tasklet configuration */
> > +	int tdm_rx_flag;
> 
> What does "tdm_rx_flag" indicate?  What about "tasklet_conf"?
> 
> > +	struct list_head myports;	/* list of ports, created on this
> > +					 adapter */
> > +	struct list_head list;
> 
> If you've got more than one list, it's probably a bad idea for any of
> them to be simply called "list".
> 
> > +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> 
> Comments should add new information, not just restate what the code
> already said.
> 
> > +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev) {
> > +	return dev->data;
> > +}
> > +
> > +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void
> > +*data) {
> > +	dev->data = data;
> > +}
> 
> Is this really needed?
> 
> > +
> > +/* functions exported by tdm.o */
> > +
> > +extern int tdm_add_adapter(struct tdm_adapter *); extern int
> > +tdm_del_adapter(struct tdm_adapter *); extern int
> > +tdm_register_driver(struct tdm_driver *); extern void
> > +tdm_del_driver(struct tdm_driver *); extern void
> > +tdm_unregister_driver(struct tdm_driver *); extern void
> > +init_config_adapter(struct tdm_adapter *);
> > +
> > +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> > +extern unsigned int tdm_port_close(void *); extern unsigned int
> > +tdm_port_ioctl(void *, unsigned int, unsigned long); extern unsigned
> > +int tdm_channel_read(void *, void *, void *, u16 *); extern unsigned
> > +int tdm_channel_write(void *, void * , void *, u16); extern unsigned
> > +int tdm_port_poll(void *, unsigned int);
> > +
> > +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> > +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> > +						struct tdm_channel *);
> 
> Please provide parameter names with prototypes.
> 
> The "extern" is unnecessary.
> 
> > +static inline int tdm_add_driver(struct tdm_driver *driver) {
> > +	return tdm_register_driver(driver);
> > +}
> 
> What is the semantic difference between tdm_add_driver() and
> tdm_register_driver()?  Why do they both exist?
> 
> -Scott
diff mbox

Patch

diff --git a/drivers/Kconfig b/drivers/Kconfig
index ad6c1eb..25f7f5b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,4 +130,5 @@  source "drivers/virt/Kconfig"
 
 source "drivers/net/dpa/NetCommSw/Kconfig"
 
+source "drivers/tdm/Kconfig"
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index cd546eb..362b5ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -102,6 +102,7 @@  obj-$(CONFIG_INFINIBAND)	+= infiniband/
 obj-$(CONFIG_SGI_SN)		+= sn/
 obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
+obj-$(CONFIG_TDM)		+= tdm/
 obj-$(CONFIG_SUPERH)		+= sh/
 obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
 ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig
new file mode 100644
index 0000000..8db2b05
--- /dev/null
+++ b/drivers/tdm/Kconfig
@@ -0,0 +1,25 @@ 
+#
+# TDM subsystem configuration
+#
+
+menuconfig TDM
+	tristate "TDM support"
+	---help---
+	  More information is contained in the directory <file:Documentation/tdm/>,
+	  especially in the file called "summary" there.
+	  If you want TDM support, you should say Y here and also to the
+	  specific driver for your bus adapter(s) below.
+
+	  This TDM support can also be built as a module.  If so, the module
+	  will be called tdm-core.
+
+if TDM
+
+config TDM_DEBUG_CORE
+	bool "TDM Core debugging messages"
+	help
+	  Say Y here if you want the TDM core to produce a bunch of debug
+	  messages to the system log.  Select this if you are having a
+	  problem with TDM support and want to see more of what is going on.
+
+endif # TDM
diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
new file mode 100644
index 0000000..cdda260
--- /dev/null
+++ b/drivers/tdm/tdm-core.c
@@ -0,0 +1,1146 @@ 
+/* driver/tdm/tdm-core.c
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
+ *
+ * TDM core is the interface between TDM clients and TDM devices.
+ * It is also intended to serve as an interface for line controld
+ * devices later on.
+ *
+ * Author:Hemant Agrawal <hemant@freescale.com>
+ *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
+ *
+ * Modified by Sandeep Kr Singh <sandeep@freescale.com>
+ *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
+ * 1. Added framework based initialization of device.
+ * 2. All the init/run time configuration is now done by framework.
+ * 3. Added channel level operations.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* if read write debug required */
+#undef TDM_CORE_DEBUG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tdm.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/hardirq.h>
+#include <linux/irqflags.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include "device/tdm_fsl.h"
+
+
+static DEFINE_MUTEX(tdm_core_lock);
+static DEFINE_IDR(tdm_adapter_idr);
+/* List of TDM adapters registered with TDM framework */
+LIST_HEAD(adapter_list);
+
+/* List of TDM clients registered with TDM framework */
+LIST_HEAD(driver_list);
+
+/* In case the previous data is not fetched by the client driver, the
+ * de-interleaving function will  discard the old data and rewrite the
+ * new data */
+static int use_latest_tdm_data = 1;
+
+/* this tasklet is created for each adapter instance */
+static void tdm_data_tasklet_fn(unsigned long);
+
+/* tries to match client driver with the adapter */
+static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
+{
+	/* match on an id table if there is one */
+	if (driver->id_table && driver->id_table->name[0]) {
+		if (!(strcmp(driver->id_table->name, adap->name)))
+			return (int)driver->id_table;
+	}
+	return TDM_E_OK;
+}
+
+static int tdm_attach_driver_adap(struct tdm_driver *driver,
+					struct tdm_adapter *adap)
+{
+	/* if driver is already attached to any other adapter, return*/
+	if (driver->adapter && (driver->adapter != adap))
+		return TDM_E_OK;
+
+	driver->adapter = adap;
+
+	if (driver->attach_adapter) {
+		if (driver->attach_adapter(adap) < 0)
+			/* We ignore the return code; if it fails, too bad */
+			pr_err("attach_adapter failed for driver [%s]\n",
+				driver->name);
+	}
+	adap->drv_count++;
+
+	if (!adap->tasklet_conf) {
+		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
+						(unsigned long)adap);
+		adap->tasklet_conf = 1;
+	}
+
+	return TDM_E_OK;
+}
+
+/* Detach client driver and adapter */
+static int tdm_detach_driver_adap(struct tdm_driver *driver,
+					struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+
+	if (!driver->adapter || (driver->adapter != adap))
+		return TDM_E_OK;
+
+	if (!driver->detach_adapter)
+		return TDM_E_OK;
+
+	adap->drv_count--;
+
+	/* If no more driver is registed with the adapter*/
+	if (!adap->drv_count && adap->tasklet_conf) {
+		tasklet_disable(&adap->tdm_data_tasklet);
+		tasklet_kill(&adap->tdm_data_tasklet);
+		adap->tasklet_conf = 0;
+	}
+
+	if (driver->detach_adapter) {
+		if (driver->detach_adapter(adap))
+			pr_err("detach_adapter failed for driver [%s]\n",
+				driver->name);
+	}
+
+	driver->adapter = NULL;
+	return res;
+}
+
+/* TDM adapter Registration/De-registration with TDM framework */
+
+static int tdm_register_adapter(struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+	struct tdm_driver *driver, *next;
+
+	if (!adap) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_init(&adap->adap_lock);
+	INIT_LIST_HEAD(&adap->myports);
+	spin_lock_init(&adap->portlist_lock);
+
+	adap->drv_count = 0;
+	adap->tasklet_conf = 0;
+
+	list_add_tail(&adap->list, &adapter_list);
+
+	/* initialization of driver by framework in default configuration */
+	init_config_adapter(adap);
+
+	/* Notify drivers */
+	pr_info("adapter [%s] registered\n", adap->name);
+	mutex_lock(&tdm_core_lock);
+	list_for_each_entry_safe(driver, next, &driver_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			res = tdm_attach_driver_adap(driver, adap);
+			pr_info(
+			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
+				driver->id, adap->name, adap->id);
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	return res;
+}
+
+/*
+ * tdm_add_adapter - declare tdm adapter, use dynamic device number
+ * @adapter: the adapter to add
+ * Context: can sleep
+ *
+ * This routine is used to declare a TDM adapter
+ * When this returns zero, a new device number will be allocated and stored
+ * in adap->id, and the specified adapter became available for the clients.
+ * Otherwise, a negative errno value is returned.
+ */
+int tdm_add_adapter(struct tdm_adapter *adapter)
+{
+	int id, res = TDM_E_OK;
+	if (!adapter) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+retry:
+	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
+		return -ENOMEM;
+
+	mutex_lock(&tdm_core_lock);
+	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
+	mutex_unlock(&tdm_core_lock);
+
+	if (res < 0) {
+		if (res == -EAGAIN)
+			goto retry;
+		return res;
+	}
+
+	adapter->id = id;
+	return tdm_register_adapter(adapter);
+}
+EXPORT_SYMBOL(tdm_add_adapter);
+
+
+/**
+ * tdm_del_adapter - unregister TDM adapter
+ * @adap: the adapter being unregistered
+ *
+ * This unregisters an TDM adapter which was previously registered
+ * by @tdm_add_adapter.
+ */
+int tdm_del_adapter(struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+	struct tdm_adapter *found;
+	struct tdm_driver *driver, *next;
+
+	if (!adap) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	/* First make sure that this adapter was ever added */
+	mutex_lock(&tdm_core_lock);
+	found = idr_find(&tdm_adapter_idr, adap->id);
+	mutex_unlock(&tdm_core_lock);
+	if (found != adap) {
+		pr_err("tdm-core: attempting to delete unregistered "
+			 "adapter [%s]\n", adap->name);
+		return -EINVAL;
+	}
+
+	/*disable and kill the data processing tasklet */
+	if (adap->tasklet_conf) {
+		tasklet_disable(&adap->tdm_data_tasklet);
+		tasklet_kill(&adap->tdm_data_tasklet);
+		adap->tasklet_conf = 0;
+	}
+
+	/* Detach any active ports. This can't fail, thus we do not
+	   checking the returned value. */
+	mutex_lock(&tdm_core_lock);
+	list_for_each_entry_safe(driver, next, &driver_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			tdm_detach_driver_adap(driver, adap);
+			pr_info(
+			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
+				 driver->id, adap->name, adap->id);
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	mutex_lock(&tdm_core_lock);
+	idr_remove(&tdm_adapter_idr, adap->id);
+	mutex_unlock(&tdm_core_lock);
+
+	pr_debug("adapter [%s] unregistered\n", adap->name);
+
+	list_del(&adap->list);
+	/* Clear the device structure in case this adapter is ever going to be
+	   added again */
+	adap->parent = NULL;
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_del_adapter);
+
+/* TDM Client Drivers Registration/De-registration Functions */
+int tdm_register_driver(struct tdm_driver *driver)
+{
+	int res = TDM_E_OK;
+	struct tdm_adapter *adap, *next;
+
+	list_add_tail(&driver->list, &driver_list);
+
+	mutex_lock(&tdm_core_lock);
+	/* Walk the adapters that are already present */
+	list_for_each_entry_safe(adap, next, &adapter_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			res = tdm_attach_driver_adap(driver, adap);
+			pr_info("TDM Driver(ID=%d)is attached with Adapter"
+				"%s(ID = %d) drv_count=%d", driver->id,
+				adap->name, adap->id, adap->drv_count);
+		break;
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_register_driver);
+
+/*
+ * tdm_unregister_driver - unregister TDM client driver from TDM framework
+ * @driver: the driver being unregistered
+ */
+void tdm_unregister_driver(struct tdm_driver *driver)
+{
+	if (!driver) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return;
+	}
+       /* A driver can register to only one adapter,
+	* so no need to browse the list */
+	mutex_lock(&tdm_core_lock);
+	tdm_detach_driver_adap(driver, driver->adapter);
+	mutex_unlock(&tdm_core_lock);
+
+	list_del(&driver->list);
+
+	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
+}
+EXPORT_SYMBOL(tdm_unregister_driver);
+
+/* TDM Framework init and exit */
+static int __init tdm_init(void)
+{
+	pr_info("%s\n", __func__);
+	return TDM_E_OK;
+}
+
+static void __exit tdm_exit(void)
+{
+	pr_info("%s\n", __func__);
+	return;
+}
+
+/* We must initialize early, because some subsystems register tdm drivers
+ * in subsys_initcall() code, but are linked (and initialized) before tdm.
+ */
+postcore_initcall(tdm_init);
+module_exit(tdm_exit);
+
+
+/* Interface to the tdm device/adapter */
+
+/* tdm_adap_send - issue a TDM write
+ * @adap: Handle to TDM device
+ * @buf: Data that will be written to the TDM device
+ * @count: How many bytes to write
+ *
+ * Returns negative errno, or else the number of bytes written.
+ */
+int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count)
+{
+	int res;
+
+	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	if (adap->algo->tdm_write)
+		res = adap->algo->tdm_write(adap, buf, count);
+	else {
+		pr_err("TDM level write not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* If everything went ok (i.e. frame transmitted), return #bytes
+	   transmitted, else error code. */
+	return (res == 1) ? count : res;
+}
+EXPORT_SYMBOL(tdm_adap_send);
+
+/**
+ * tdm_adap_recv - issue a TDM read
+ * @adap: Handle to TDM device
+ * @buf: Where to store data read from TDM device
+ *
+ * Returns negative errno, or else the number of bytes read.
+ */
+int tdm_adap_recv(struct tdm_adapter *adap, void **buf)
+{
+	int res;
+
+	if (adap->algo->tdm_read)
+		res = adap->algo->tdm_read(adap, (u16 **)buf);
+	else {
+		pr_err("TDM level read not supported\n");
+		return -EOPNOTSUPP;
+	}
+	/* If everything went ok (i.e. frame received), return #bytes
+	   transmitted, else error code. */
+	return res;
+}
+
+/**
+ * tdm_adap_get_write_buf - get next write TDM device buffer
+ * @adap: Handle to TDM device
+ * @buf: pointer to TDM device buffer
+ *
+ * Returns negative errno, or else size of the write buffer.
+ */
+int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf)
+{
+	int res;
+
+	if (adap->algo->tdm_get_write_buf) {
+		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
+	} else {
+		pr_err("TDM level write buf get not supported\n");
+		return -EOPNOTSUPP;
+	}
+	/* If everything went ok (i.e. 1 msg received), return #bytes
+	   transmitted, else error code. */
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_get_write_buf);
+
+int tdm_adap_enable(struct tdm_driver *drv)
+{
+	int res;
+	struct tdm_adapter *adap;
+	if (drv == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	adap = drv->adapter;
+
+	if (adap->algo->tdm_enable) {
+		res = adap->algo->tdm_enable(adap);
+	} else {
+		pr_err("TDM level enable not supported\n");
+		return -EOPNOTSUPP;
+	}
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_enable);
+
+int tdm_adap_disable(struct tdm_driver *drv)
+{
+	int res;
+	struct tdm_adapter *adap;
+	if (drv == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	adap = drv->adapter;
+
+	if (adap->algo->tdm_disable) {
+		res = adap->algo->tdm_disable(adap);
+	} else {
+		pr_err("TDM level enable not supported\n");
+		return -EOPNOTSUPP;
+	}
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_disable);
+
+struct tdm_adapter *tdm_get_adapter(int id)
+{
+	struct tdm_adapter *adapter;
+
+	mutex_lock(&tdm_core_lock);
+	adapter = idr_find(&tdm_adapter_idr, id);
+	if (adapter && !try_module_get(adapter->owner))
+		adapter = NULL;
+
+	mutex_unlock(&tdm_core_lock);
+
+	return adapter;
+}
+EXPORT_SYMBOL(tdm_get_adapter);
+
+void tdm_put_adapter(struct tdm_adapter *adap)
+{
+	module_put(adap->owner);
+}
+EXPORT_SYMBOL(tdm_put_adapter);
+
+
+/* Port Level APIs of TDM Framework */
+unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)
+{
+	struct tdm_port *port;
+	struct tdm_adapter *adap;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+
+	if (driver == NULL) {
+		pr_err("driver NULL\n");
+		return -ENODEV;
+	}
+	if (driver->adapter == NULL) {
+		pr_err("adapter NULL\n");
+		return -ENODEV;
+	}
+
+	adap = tdm_get_adapter(driver->adapter->id);
+	if (!adap)
+		return -ENODEV;
+
+	/* This creates an anonymous tdm_port, which may later be
+	 * pointed to some slot.
+	 *
+	 */
+	port = kzalloc(sizeof(*port), GFP_KERNEL);
+	if (!port) {
+		res = -ENOMEM;
+		goto out;
+	}
+
+	init_waitqueue_head(&port->ch_wait_queue);
+
+
+	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
+	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
+
+	port->in_use = 1;
+
+	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
+	port->driver = driver;
+	port->adapter = adap;
+
+	spin_lock_irqsave(&adap->portlist_lock, flags);
+	list_add_tail(&port->list, &adap->myports);
+	spin_unlock_irqrestore(&adap->portlist_lock, flags);
+
+	INIT_LIST_HEAD(&port->mychannels);
+
+	*h_port = port;
+
+out:
+	return res;
+}
+EXPORT_SYMBOL(tdm_port_open);
+
+unsigned int tdm_port_close(void *h_port)
+{
+	struct tdm_adapter *adap;
+	struct tdm_driver *driver;
+	struct tdm_port *port;
+	struct tdm_channel *temp, *channel;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+
+	driver =  port->driver;
+
+	if (driver == NULL) {
+		pr_err("driver NULL\n");
+		res = -ENODEV;
+		goto out;
+	}
+	if (driver->adapter == NULL) {
+		pr_err("adapter NULL\n");
+		res = -ENODEV;
+		goto out;
+	}
+
+	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
+	if (channel)
+		if (channel->in_use) {
+			pr_err("%s: Cannot close port. Channel in use\n",
+								__func__);
+			res = -ENXIO;
+			goto out;
+			}
+	}
+	adap = driver->adapter;
+
+	spin_lock_irqsave(&adap->portlist_lock, flags);
+	list_del(&port->list);
+	spin_unlock_irqrestore(&adap->portlist_lock, flags);
+
+	if (port->p_port_data != NULL) {
+		int i;
+		struct tdm_bd *ch_bd;
+
+		/* If the tdm is in channelised mode,
+		de-allocate the channelised buffer */
+		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
+		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
+			ch_bd->flag = 0;
+			ch_bd++;
+		}
+		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
+		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
+			ch_bd->flag = 0;
+			ch_bd++;
+		}
+		kfree(port->p_port_data);
+	}
+	kfree(port);
+	return res;
+out:
+	if (port)
+		kfree(port->p_port_data);
+	kfree(port);
+	return res;
+}
+EXPORT_SYMBOL(tdm_port_close);
+
+unsigned int tdm_channel_read(void *h_port, void *h_channel,
+				void *p_data, u16 *size)
+{
+	struct tdm_port *port;
+	struct tdm_channel *channel;
+	struct tdm_bd *rx_bd;
+	unsigned long flags;
+	int i, res = TDM_E_OK;
+	unsigned short *buf, *buf1;
+	port = (struct tdm_port *)h_port;
+	channel = (struct tdm_channel *)h_channel;
+
+	if ((port && channel) == 0) { /* invalid handle*/
+		pr_err("%s:Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	if (!port->in_use)
+		return -EIO;
+	if (!channel->p_ch_data || !channel->in_use)
+		return -EIO;
+
+	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
+	rx_bd = channel->p_ch_data->rx_out_data;
+
+	if (rx_bd->flag) {
+		*size = rx_bd->length;
+		buf = (u16 *) p_data;
+		buf1 = (u16 *)rx_bd->p_data;
+		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
+			buf[i] = buf1[i];
+		rx_bd->flag = 0;
+		rx_bd->offset = 0;
+		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
+				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
+
+	} else {
+		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
+						flags);
+		pr_info("No Data Available");
+		return -EAGAIN;
+	}
+	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock, flags);
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_read);
+
+
+unsigned int tdm_channel_write(void *h_port, void *h_channel,
+				void *p_data, u16 size)
+{
+	struct tdm_port *port;
+	struct tdm_channel *channel;
+	struct tdm_bd *tx_bd;
+	unsigned long flags;
+	int err = TDM_E_OK;
+	port = (struct tdm_port *)h_port;
+	channel = (struct tdm_channel *)h_channel;
+#ifdef TDM_CORE_DEBUG
+	bool data_flag = 0;
+#endif
+
+	if ((port && channel) == 0) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+
+	if (p_data == NULL) { /* invalid data*/
+		pr_err("Invalid Data");
+		return -EFAULT;
+	}
+
+	if (!port->in_use)
+		return -EIO;
+	if (!channel->p_ch_data || !channel->in_use)
+		return -EIO;
+
+	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
+	tx_bd = channel->p_ch_data->tx_in_data;
+
+	if (!tx_bd->flag) {
+		tx_bd->length = size;
+		memcpy(tx_bd->p_data, p_data,
+			size * port->adapter->adapt_cfg.slot_width);
+		tx_bd->flag = 1;
+		tx_bd->offset = 0;
+		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
+				channel->p_ch_data->tx_data_fifo : tx_bd+1;
+		port->port_stat.tx_pkt_count++;
+#ifdef TDM_CORE_DEBUG
+		data_flag = 1;
+#endif
+	} else {
+		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
+						flags);
+		port->port_stat.tx_pkt_drop_count++;
+		pr_err("E_NO_MEMORY -Failed Transmit");
+		return -ENOMEM;
+	}
+	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock, flags);
+
+#ifdef	TDM_CORE_DEBUG
+	if (data_flag) {
+		int k;
+		pr_info("\nTX port:%d - Write - Port TX-%d\n",
+						port->port_id, size);
+		for (k = 0; k < size; k++)
+			pr_info("%x", p_data[k]);
+		pr_info("\n");
+	}
+#endif
+	return err;
+}
+EXPORT_SYMBOL(tdm_channel_write);
+
+wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port)
+{
+	struct tdm_port *port;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return NULL;
+	}
+
+	return &port->ch_wait_queue;
+
+}
+EXPORT_SYMBOL(tdm_port_get_wait_queue);
+
+/* Driver Function for select and poll. Based on Port no, it sleeps on
+ * waitqueue */
+unsigned int tdm_port_poll(void *h_port, unsigned int wait_time)
+{
+	struct tdm_port *port;
+	unsigned long timeout = msecs_to_jiffies(wait_time);
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	if (!port->p_port_data || !port->in_use)
+		return -EIO;
+
+	if (port->p_port_data->rx_out_data->flag) {
+		pr_debug("Data Available");
+		return TDM_E_OK;
+	}
+	if (timeout) {
+		wait_event_interruptible_timeout(port->ch_wait_queue,
+					  port->p_port_data->rx_out_data->flag,
+					  timeout);
+
+		if (port->p_port_data->rx_out_data->flag) {
+			pr_debug("Data Available");
+			return TDM_E_OK;
+		}
+	}
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(tdm_port_poll);
+
+unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
+{
+	struct tdm_port *port;
+	int port_num;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL || portStat == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+	port_num =  port->port_id;
+
+	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
+
+	pr_info("TDM Port %d Get Stats", port_num);
+
+	return TDM_E_OK;
+}
+EXPORT_SYMBOL(tdm_port_get_stats);
+
+/* Data handling functions */
+
+static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
+{
+	struct tdm_port *port, *next;
+	struct tdm_channel *channel, *temp;
+	struct tdm_bd	*ch_bd;
+
+	int i, buf_size, ch_data_len;
+	u16 *input_tdm_buffer;
+	u16 *pcm_buffer;
+	int slot_width;
+	int frame_ch_data_size;
+	bool ch_data;
+	int bytes_in_fifo_per_frame;
+	int bytes_slot_offset;
+
+	ch_data_len = NUM_SAMPLES_PER_FRAME;
+	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
+	ch_data = 0;
+
+	if (!adap) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	slot_width = adap->adapt_cfg.slot_width;
+	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
+	if (buf_size <= 0 || !input_tdm_buffer)
+		return -EINVAL;
+
+	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
+	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
+
+	/* de-interleaving for all ports*/
+	list_for_each_entry_safe(port, next, &adap->myports, list) {
+
+		/* if the port is not open */
+		if (!port->in_use)
+			continue;
+
+		list_for_each_entry_safe(channel, temp, &port->mychannels,
+							list) {
+		/* if the channel is not open */
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+		ch_bd = channel->p_ch_data->rx_in_data;
+		spin_lock(&channel->p_ch_data->rx_channel_lock);
+			/*if old data is to be discarded */
+		if (use_latest_tdm_data)
+			if (ch_bd->flag) {
+				ch_bd->flag = 0;
+				ch_bd->offset = 0;
+				if (ch_bd == channel->p_ch_data->rx_out_data)
+					channel->p_ch_data->rx_out_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->rx_data_fifo
+						: ch_bd+1;
+					port->port_stat.rx_pkt_drop_count++;
+				}
+			/* if the bd is empty */
+			if (!ch_bd->flag) {
+				if (ch_bd->offset == 0)
+					ch_bd->length = port->rx_max_frames;
+
+				pcm_buffer = ch_bd->p_data + ch_bd->offset;
+				/* De-interleaving the data */
+				for (i = 0; i < ch_data_len; i++) {
+					pcm_buffer[i]
+					= input_tdm_buffer[i*bytes_slot_offset +
+						channel->ch_id];
+				}
+				ch_bd->offset += ch_data_len * slot_width;
+
+				if (ch_bd->offset >=
+					(ch_bd->length - frame_ch_data_size)*
+						(adap->adapt_cfg.slot_width)) {
+					ch_bd->flag = 1;
+					ch_bd->offset = 0;
+					channel->p_ch_data->rx_in_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->rx_data_fifo
+						: ch_bd+1;
+					ch_data = 1;
+				}
+			} else {
+				port->port_stat.rx_pkt_drop_count++;
+			}
+		spin_unlock(&channel->p_ch_data->rx_channel_lock);
+		}
+
+		if (ch_data) {
+			/*	Wake up the Port Data Poll event */
+			wake_up_interruptible(&port->ch_wait_queue);
+#ifdef	TDM_CORE_DEBUG
+			pr_info("Port RX-%d-%d\n", channel->ch_id, ch_data_len);
+			for (i = 0; i < ch_data_len; i++)
+				pr_info("%x", pcm_buffer[i]);
+			pr_info("\n");
+#endif
+			port->port_stat.rx_pkt_count++;
+			ch_data = 0;
+		}
+	}
+	return TDM_E_OK;
+}
+
+static int tdm_data_tx_interleave(struct tdm_adapter *adap)
+{
+	struct tdm_port *port, *next;
+	struct tdm_channel *channel, *temp;
+	struct tdm_bd	*ch_bd;
+	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
+	bool last_data = 0;
+	u16 *output_tdm_buffer;
+	u16 *pcm_buffer;
+	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
+	int bytes_in_fifo_per_frame;
+	int bytes_slot_offset;
+
+#ifdef TDM_CORE_DEBUG
+	u8	data_flag = 0;
+#endif
+
+	if (adap == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	buf_size = tdm_adap_get_write_buf(adap, (void **)&output_tdm_buffer);
+	if (buf_size <= 0 || !output_tdm_buffer)
+		return -EINVAL;
+
+	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
+	bytes_slot_offset = bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
+
+
+	memset(output_tdm_buffer, 0, sizeof(buf_size));
+
+	list_for_each_entry_safe(port, next, &adap->myports, list) {
+
+		/* check if the port is open */
+		if (!port->in_use)
+			continue;
+
+		list_for_each_entry_safe(channel, temp, &port->mychannels,
+								list) {
+		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
+
+
+		/* if the channel is open */
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+
+		spin_lock(&channel->p_ch_data->tx_channel_lock);
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+			ch_bd = channel->p_ch_data->tx_out_data;
+			if (ch_bd->flag) {
+				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
+						ch_bd->offset);
+				/*if the buffer has less frames than required */
+				if (frame_ch_data_size >=
+					((ch_bd->length) - (ch_bd->offset/
+						adap->adapt_cfg.slot_width))) {
+					ch_data_len =
+					(ch_bd->length) - (ch_bd->offset/
+						adap->adapt_cfg.slot_width);
+					last_data = 1;
+				} else {
+					ch_data_len = frame_ch_data_size;
+				}
+				/* Interleaving the data */
+				for (i = 0; i < ch_data_len; i++) {
+					/* TODO- need to be generic for any size
+					   assignment*/
+					output_tdm_buffer[channel->ch_id +
+						bytes_slot_offset * i] =
+								pcm_buffer[i];
+				}
+				/* If all the data of this buffer is
+							transmitted */
+				if (last_data) {
+					ch_bd->flag = 0;
+					ch_bd->offset = 0;
+					channel->p_ch_data->tx_out_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->tx_data_fifo
+						: ch_bd+1;
+					port->port_stat.tx_pkt_conf_count++;
+				} else {
+					ch_bd->offset += ch_data_len *
+						(adap->adapt_cfg.slot_width);
+				}
+#ifdef	TDM_CORE_DEBUG
+				data_flag = 1;
+#endif
+			}
+		spin_unlock(&channel->p_ch_data->tx_channel_lock);
+		}
+	}
+
+#ifdef	TDM_CORE_DEBUG
+	if (data_flag) {
+		pr_info("TX-TDM Interleaved Data-\n");
+		for (i = 0; i < 64; i++)
+			pr_info("%x", output_tdm_buffer[i]);
+		pr_info("\n");
+	  }
+#endif
+	return TDM_E_OK;
+}
+
+/* Channel Level APIs of TDM Framework */
+int tdm_channel_open(u16 chanid, u16 ch_width, struct tdm_port *port,
+				void **h_channel)
+{
+	struct tdm_channel *channel, *temp;
+	unsigned long		flags;
+	struct tdm_ch_data	*p_ch_data;
+	int res = TDM_E_OK;
+
+	if (!(port && h_channel)) {
+		pr_err("%s: Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ch_width != 1) {
+		pr_err("%s: Mode not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
+		if (channel->ch_id == chanid) {
+			pr_err("%s: Channel %d already open\n",
+						__func__, chanid);
+			return -EINVAL;
+		}
+	}
+
+	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+	if (!channel) {
+		res = -ENOMEM;
+		goto out;
+	}
+
+	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
+	if (!p_ch_data) {
+		res = -ENOMEM;
+		goto outdata;
+	}
+
+	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
+	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
+
+	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
+	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
+	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
+	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
+	spin_lock_init(&p_ch_data->rx_channel_lock);
+	spin_lock_init(&p_ch_data->tx_channel_lock);
+
+	channel->p_ch_data = p_ch_data;
+
+	channel->ch_id = chanid;
+	channel->ch_cfg.first_slot = chanid;
+	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized mode and
+						configurable for other modes */
+	channel->port = port;
+	channel->in_use = 1;
+
+	spin_lock_irqsave(&port->ch_list_lock, flags);
+	list_add_tail(&channel->list, &port->mychannels);
+	spin_unlock_irqrestore(&port->ch_list_lock, flags);
+
+	*h_channel = channel;
+
+	return res;
+
+outdata:
+	kfree(channel);
+out:
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_open);
+
+int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
+				struct tdm_channel *h_channel)
+{
+	struct tdm_channel *channel;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+	channel = h_channel;
+
+	if (!(port && channel)) {
+		pr_err("%s: Invalid handle\n", __func__);
+		res = -EINVAL;
+		goto out;
+	}
+
+	if (ch_width != 1) {
+		pr_err("%s: Mode not supported\n", __func__);
+		res = -EINVAL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&port->ch_list_lock, flags);
+	list_del(&channel->list);
+	spin_unlock_irqrestore(&port->ch_list_lock, flags);
+
+out:
+	if (channel)
+		kfree(channel->p_ch_data);
+	kfree(channel);
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_close);
+
+void init_config_adapter(struct tdm_adapter *adap)
+{
+	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
+		.loopback = e_TDM_PROCESS_NORMAL,
+		.num_ch = NUM_CHANNELS,
+		.ch_size_type = CHANNEL_16BIT_LIN,
+		.frame_len = NUM_SAMPLES_PER_FRAME,
+		.num_frames = NUM_SAMPLES_PER_FRAME,
+		.adap_mode = e_TDM_ADAPTER_MODE_NONE
+			 };
+
+	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 + 1;
+
+	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
+		sizeof(struct fsl_tdm_adapt_cfg));
+
+	return;
+}
+EXPORT_SYMBOL(init_config_adapter);
+
+static void tdm_data_tasklet_fn(unsigned long data)
+{
+	struct tdm_adapter *adapter;
+	adapter = (struct tdm_adapter *)data;
+	if (adapter != NULL) {
+		tdm_data_tx_interleave(adapter);
+		tdm_data_rx_deinterleave(adapter);
+	}
+}
+
+
+MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
+	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
+MODULE_DESCRIPTION("TDM Driver Framework Core");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index ae28e93..dc1a655 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -416,6 +416,17 @@  struct i2c_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* tdm */
+
+#define TDM_NAME_SIZE   20
+#define TDM_MODULE_PREFIX "tdm:"
+
+struct tdm_device_id {
+	char name[TDM_NAME_SIZE];
+	kernel_ulong_t driver_data      /* Data private to the driver */
+			__attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+
 /* spi */
 
 #define SPI_NAME_SIZE	32
diff --git a/include/linux/tdm.h b/include/linux/tdm.h
new file mode 100644
index 0000000..8cf4ef5
--- /dev/null
+++ b/include/linux/tdm.h
@@ -0,0 +1,347 @@ 
+/* include/linux/tdm.h
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
+ *
+ * tdm.h - definitions for the tdm-device framework interface
+ *
+ * Author:Hemant Agrawal <hemant@freescale.com>
+ *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef _LINUX_TDM_H
+#define _LINUX_TDM_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>	/* for struct device */
+#include <linux/sched.h>	/* for completion */
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+
+#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
+#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
+#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
+#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
+
+#define NUM_CHANNELS		16
+#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
+						 channel. Req for voice data */
+#define NUM_MS			10
+#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
+						samples for 1 client buffer */
+#define NUM_OF_TDM_BUF		3
+
+/* General options */
+
+struct tdm_adapt_algorithm;
+struct tdm_adapter;
+struct tdm_port;
+struct tdm_driver;
+
+/* Align addr on a size boundary - adjust address up if needed */
+/* returns min value greater than size which is multiple of alignment */
+static inline int ALIGN_SIZE(u64 size, u32 alignment)
+{
+	return (size + alignment - 1) & (~(alignment - 1));
+}
+
+/**
+ * struct tdm_driver - represent an TDM device driver
+ * @class: What kind of tdm device we instantiate (for detect)
+ * @id:Driver id
+ * @name: Name of the driver
+ * @attach_adapter: Callback for device addition (for legacy drivers)
+ * @detach_adapter: Callback for device removal (for legacy drivers)
+ * @probe: Callback for device binding
+ * @remove: Callback for device unbinding
+ * @shutdown: Callback for device shutdown
+ * @suspend: Callback for device suspend
+ * @resume: Callback for device resume
+ * @command: Callback for sending commands to device
+ * @id_table: List of TDM devices supported by this driver
+ * @list: List of drivers created (for tdm-core use only)
+ */
+struct tdm_driver {
+	unsigned int class;
+	unsigned int id;
+	char name[TDM_NAME_SIZE];
+
+	int (*attach_adapter)(struct tdm_adapter *);
+	int (*detach_adapter)(struct tdm_adapter *);
+
+	/* Standard driver model interfaces */
+	int (*probe)(const struct tdm_device_id *);
+	int (*remove)(void);
+
+	/* driver model interfaces that don't relate to enumeration */
+	void (*shutdown)(void);
+	int (*suspend)(pm_message_t mesg);
+	int (*resume)(void);
+
+	/* a ioctl like command that can be used to perform specific functions
+	 * with the device.
+	 */
+	int (*command)(unsigned int cmd, void *arg);
+
+	const struct tdm_device_id *id_table;
+
+	/* The associated adapter for this driver */
+	struct tdm_adapter *adapter;
+	struct list_head list;
+};
+
+/* tdm per port statistics structure, used for providing and storing tdm port
+ * statistics.
+ */
+struct tdm_port_stats {
+	unsigned int rx_pkt_count;	/* Rx frame count per channel */
+	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
+					 clean space for new buffer */
+	unsigned int tx_pkt_count;	/* Tx frame count per channel */
+	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count per
+					 channel */
+	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel due to
+					 queue full */
+};
+
+
+/* tdm Buffer Descriptor, used for Creating Interleaved and De-interleaved
+ * FIFOs
+ */
+struct tdm_bd {
+	unsigned char flag;		/* BD is full or empty */
+	unsigned char wrap;		/* BD is last in the queue */
+	unsigned short length;	/* Length of Data in BD */
+	/*TODO: use dyanmic memory */
+	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
+	unsigned long offset;	/* Offset of the Data Pointer to be used */
+};
+
+#define TDM_CH_RX_BD_RING_SIZE	3
+#define TDM_CH_TX_BD_RING_SIZE	3
+
+/* tdm RX-TX Channelised Data */
+struct tdm_port_data {
+	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel Data
+								BD Ring */
+	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled by
+						de-interleave function */
+	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
+							read by App */
+	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel Data
+								BD Ring */
+	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
+						 filled by App */
+	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read by
+						interleave function */
+	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
+	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
+};
+
+/* structure tdm_port_cfg - contains configuration params for a port */
+struct tdm_port_cfg {
+	unsigned short port_mode;
+};
+
+/* struct tdm_port - represent an TDM ports for a device */
+struct tdm_port {
+	unsigned short port_id;
+	unsigned short in_use;		/* Port is enabled? */
+	uint16_t rx_max_frames;		/* Received Port frames
+					 before allowing Read Operation in
+					 Port Mode */
+
+	struct tdm_port_stats port_stat;/* A structure parameters defining
+					 TDM port statistics. */
+	struct tdm_port_data *p_port_data;	/* a structure parameters
+						defining tdm channelised data */
+	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data */
+
+	struct tdm_driver *driver;	/* driver for this port */
+	struct tdm_adapter *adapter;	/* adapter for this port */
+	struct list_head list;		/* list of ports */
+	struct list_head mychannels;	/* list of channels, created on this
+					 port*/
+	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
+	struct tdm_port_cfg port_cfg;/* A structure parameters defining
+					 TDM port configuration. */
+};
+
+/* tdm RX-TX Channelised Data */
+struct tdm_ch_data {
+	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data BD
+								Ring */
+	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
+						de-interleave function */
+	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App */
+	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data BD
+								Ring */
+	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
+						App */
+	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
+						interleave function */
+	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
+	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
+};
+
+/* Channel config params */
+struct tdm_ch_cfg {
+	unsigned short num_slots;
+	unsigned short first_slot;
+};
+
+/* struct tdm_channel- represent a TDM channel for a port */
+struct tdm_channel {
+	u16 ch_id;			/* logical channel number */
+	struct list_head list;		/* list of channels in a port*/
+	struct tdm_port *port;		/* port for this channel */
+	u16 in_use;			/* channel is enabled? */
+	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
+	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
+};
+
+/* tdm_adapt_algorithm is for accessing the routines of device */
+struct tdm_adapt_algorithm {
+	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
+	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
+	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
+	int (*tdm_enable)(struct tdm_adapter *);
+	int (*tdm_disable)(struct tdm_adapter *);
+};
+
+/* tdm_adapter_mode is to define in mode of the device */
+enum tdm_adapter_mode {
+	e_TDM_ADAPTER_MODE_NONE = 0x00,
+	e_TDM_ADAPTER_MODE_T1 = 0x01,
+	e_TDM_ADAPTER_MODE_E1 = 0x02,
+	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
+	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
+};
+
+/* tdm_port_mode defines the mode in which the port is configured to operate
+ * It can be channelized/full/fractional.
+ */
+enum tdm_port_mode {
+	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
+	, e_TDM_PORT_FULL = 1		/* Full mode */
+	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
+};
+
+/* tdm_process_mode used for testing the tdm device in normal mode or internal
+ * loopback or external loopback
+ */
+enum tdm_process_mode {
+	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
+	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
+	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
+};
+
+
+/* TDM configuration parameters */
+struct fsl_tdm_adapt_cfg {
+	u8 num_ch;		/* Number of channels in this adpater */
+	u8 ch_size_type;		/* reciever/transmit channel
+						size for all channels */
+	u8 slot_width;		/* 1 or 2 Is defined by channel type */
+	u8 frame_len;		/* Length of frame in samples */
+	u32 num_frames;
+	u8 loopback;			/* loopback or normal */
+	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
+						4 = E1-FULL */
+	int max_num_ports;		/* Not Used: Max Number of ports that
+					can be created on this adapter */
+	int max_timeslots;		/* Max Number of timeslots that are
+					supported on this adapter */
+};
+
+/*
+ * tdm_adapter is the structure used to identify a physical tdm device along
+ * with the access algorithms necessary to access it.
+ */
+struct tdm_adapter {
+	struct module *owner;	/* owner of the adapter module */
+	unsigned int id;	/* Adapter Id */
+	unsigned int class;	/* classes to allow probing for */
+	unsigned int drv_count;	/* Number of drivers associated with the
+				 adapter */
+
+	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
+						 adapter*/
+
+	char name[TDM_NAME_SIZE];	/* Name of Adapter */
+	struct mutex adap_lock;
+	struct device *parent;		/*Not Used*/
+
+	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
+						 data processing*/
+	int tasklet_conf;	/* flag for tasklet configuration */
+	int tdm_rx_flag;
+
+	struct list_head myports;	/* list of ports, created on this
+					 adapter */
+	struct list_head list;
+	spinlock_t portlist_lock;	/* Spin Lock for port_list */
+	void *data;
+	struct fsl_tdm_adapt_cfg adapt_cfg;
+};
+
+static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
+{
+	return dev->data;
+}
+
+static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
+{
+	dev->data = data;
+}
+
+/* functions exported by tdm.o */
+
+extern int tdm_add_adapter(struct tdm_adapter *);
+extern int tdm_del_adapter(struct tdm_adapter *);
+extern int tdm_register_driver(struct tdm_driver *);
+extern void tdm_del_driver(struct tdm_driver *);
+extern void tdm_unregister_driver(struct tdm_driver *);
+extern void init_config_adapter(struct tdm_adapter *);
+
+extern unsigned int tdm_port_open(struct tdm_driver *, void **);
+extern unsigned int tdm_port_close(void *);
+extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
+extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
+extern unsigned int tdm_channel_write(void *, void * , void *, u16);
+extern unsigned int tdm_port_poll(void *, unsigned int);
+
+extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
+extern int tdm_channel_close(u16, u16, struct tdm_port *,
+						struct tdm_channel *);
+
+static inline int tdm_add_driver(struct tdm_driver *driver)
+{
+	return tdm_register_driver(driver);
+}
+
+extern struct tdm_adapter *tdm_get_adapter(int id);
+extern void tdm_put_adapter(struct tdm_adapter *adap);
+
+#endif /* __KERNEL__ */
+
+#define TDM_E_OK 0
+
+#endif /* _LINUX_TDM_H */