From 1924c3dd54cb921c0fe2fc721f2455aaef0e527c Mon Sep 17 00:00:00 2001 From: Carlos Chinea Date: Mon, 17 May 2010 19:46:08 +0300 Subject: CMT: Introduces the Nokia CMT driver. Add the Nokia CMT driver. The CMT driver takes care of the APE-CMT hardware outside the SSI/HSI communication. The initial implementation introduces a CMT reset notification interface for protocols drivers like cmt_speech and ssi_protocol. Signed-off-by: Carlos Chinea Signed-off-by: Kai Vehmanen Signed-off-by: Joni Lapilainen --- drivers/misc/cmt/Kconfig | 9 ++ drivers/misc/cmt/Makefile | 4 + drivers/misc/cmt/cmt.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/cmt.h | 53 +++++++++++ 4 files changed, 289 insertions(+) create mode 100644 drivers/misc/cmt/Kconfig create mode 100644 drivers/misc/cmt/Makefile create mode 100644 drivers/misc/cmt/cmt.c create mode 100644 include/linux/cmt.h diff --git a/drivers/misc/cmt/Kconfig b/drivers/misc/cmt/Kconfig new file mode 100644 index 000000000000..a339d79c1709 --- /dev/null +++ b/drivers/misc/cmt/Kconfig @@ -0,0 +1,9 @@ +# +# CMT +# +config CMT + tristate "Enable CMT support" + ---help--- + If you say Y here, you will enable CMT support. + + If unsure, say Y, or else you will not be able to use the CMT. diff --git a/drivers/misc/cmt/Makefile b/drivers/misc/cmt/Makefile new file mode 100644 index 000000000000..b38816aa61f4 --- /dev/null +++ b/drivers/misc/cmt/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for CMT module +# +obj-$(CONFIG_CMT) += cmt.o diff --git a/drivers/misc/cmt/cmt.c b/drivers/misc/cmt/cmt.c new file mode 100644 index 000000000000..9efcd9b595c4 --- /dev/null +++ b/drivers/misc/cmt/cmt.c @@ -0,0 +1,223 @@ +/* + * cmt.c + * + * CMT support. + * + * Copyright (C) 2009 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct cmt_device - CMT device data + * @cmt_rst_ind_tasklet: Bottom half for CMT reset line events + * @cmt_rst_ind_gpio: GPIO number of the CMT reset line + * @n_head: List of notifiers registered to get CMT events + * @node: Link on the list of available CMTs + * @device: Reference to the CMT platform device + */ +struct cmt_device { + struct tasklet_struct cmt_rst_ind_tasklet; + unsigned int cmt_rst_ind_gpio; + struct atomic_notifier_head n_head; + struct list_head node; + struct device *device; +}; + +static LIST_HEAD(cmt_list); /* List of CMT devices */ + +int cmt_notifier_register(struct cmt_device *cmtdev, struct notifier_block *nb) +{ + struct cmt_device *cmt; + int err = -ENODEV; + + if ((!cmtdev) || (!nb)) + return -EINVAL; + + list_for_each_entry(cmt, &cmt_list, node) + if (cmt == cmtdev) { + err = atomic_notifier_chain_register(&cmt->n_head, nb); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(cmt_notifier_register); + +int cmt_notifier_unregister(struct cmt_device *cmtdev, + struct notifier_block *nb) +{ + struct cmt_device *cmt; + int err = -ENODEV; + + if ((!cmtdev) || (!nb)) + return -EINVAL; + + list_for_each_entry(cmt, &cmt_list, node) + if (cmt == cmtdev) { + err = atomic_notifier_chain_unregister(&cmt->n_head, + nb); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(cmt_notifier_unregister); + +struct cmt_device *cmt_get(const char *name) +{ + struct cmt_device *p, *cmt = ERR_PTR(-ENODEV); + + list_for_each_entry(p, &cmt_list, node) + if (strcmp(name, dev_name(p->device)) == 0) { + cmt = p; + break; + } + + return cmt; +} +EXPORT_SYMBOL_GPL(cmt_get); + +void cmt_put(struct cmt_device *cmtdev) +{ +} +EXPORT_SYMBOL_GPL(cmt_put); + +static void do_cmt_rst_ind_tasklet(unsigned long cmtdev) +{ + struct cmt_device *cmt = (struct cmt_device *)cmtdev; + + dev_dbg(cmt->device, "*** CMT rst line change detected (%d) ***\n", + gpio_get_value(cmt->cmt_rst_ind_gpio)); + atomic_notifier_call_chain(&cmt->n_head, CMT_RESET, NULL); +} + +static irqreturn_t cmt_rst_ind_isr(int irq, void *cmtdev) +{ + struct cmt_device *cmt = (struct cmt_device *)cmtdev; + + tasklet_schedule(&cmt->cmt_rst_ind_tasklet); + + return IRQ_HANDLED; +} + +static int __init cmt_probe(struct platform_device *pd) +{ + struct cmt_platform_data *pdata = pd->dev.platform_data; + struct cmt_device *cmt; + int irq; + int err; + + if (!pdata) { + pr_err("CMT: No platform_data found on cmt device\n"); + return -ENXIO; + } + cmt = kzalloc(sizeof(*cmt), GFP_KERNEL); + if (!cmt) { + dev_err(&pd->dev, "Could not allocate memory for cmtdev\n"); + return -ENOMEM; + } + + cmt->device = &pd->dev; + cmt->cmt_rst_ind_gpio = pdata->cmt_rst_ind_gpio; + err = gpio_request(cmt->cmt_rst_ind_gpio, "cmt_rst_ind"); + if (err < 0) { + dev_err(&pd->dev, "Request cmt_rst_ind gpio%d failed\n", + cmt->cmt_rst_ind_gpio); + goto rback1; + } + gpio_direction_input(cmt->cmt_rst_ind_gpio); + tasklet_init(&cmt->cmt_rst_ind_tasklet, do_cmt_rst_ind_tasklet, + (unsigned long)cmt); + irq = gpio_to_irq(cmt->cmt_rst_ind_gpio); + err = request_irq(irq, cmt_rst_ind_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, "cmt_rst_ind", cmt); + if (err < 0) { + dev_err(&pd->dev, "Request cmt_rst_ind irq(%d) failed\n", irq); + goto rback2; + } + enable_irq_wake(irq); + ATOMIC_INIT_NOTIFIER_HEAD(&cmt->n_head); + list_add(&cmt->node, &cmt_list); + platform_set_drvdata(pd, cmt); + + return 0; +rback2: + gpio_free(cmt->cmt_rst_ind_gpio); +rback1: + kfree(cmt); + + return err; +} + +static int __exit cmt_remove(struct platform_device *pd) +{ + struct cmt_device *cmt = platform_get_drvdata(pd); + + if (!cmt) + return 0; + platform_set_drvdata(pd, NULL); + list_del(&cmt->node); + disable_irq_wake(gpio_to_irq(cmt->cmt_rst_ind_gpio)); + free_irq(gpio_to_irq(cmt->cmt_rst_ind_gpio), cmt); + tasklet_kill(&cmt->cmt_rst_ind_tasklet); + gpio_free(cmt->cmt_rst_ind_gpio); + kfree(cmt); + + return 0; +} + +static struct platform_driver cmt_driver = { + .remove = __exit_p(cmt_remove), + .driver = { + .name = "cmt", + .owner = THIS_MODULE, + }, +}; + +static int __init cmt_init(void) +{ + pr_notice("CMT driver\n"); + + return platform_driver_probe(&cmt_driver, cmt_probe); +} +module_init(cmt_init); + +static void __exit cmt_exit(void) +{ + pr_notice("CMT driver exited\n"); + platform_driver_unregister(&cmt_driver); +} +module_exit(cmt_exit); + +MODULE_AUTHOR("Carlos Chinea, Nokia"); +MODULE_DESCRIPTION("CMT related support"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/cmt.h b/include/linux/cmt.h new file mode 100644 index 000000000000..90b3f5433f92 --- /dev/null +++ b/include/linux/cmt.h @@ -0,0 +1,53 @@ +/* + * cmt.h + * + * CMT support header + * + * Copyright (C) 2009 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __NOKIA_CMT_H__ +#define __NOKIA_CMT_H__ + +#include + +/* + * NOKIA CMT notifier events + */ +enum { + CMT_RESET, +}; + +struct cmt_device; + +/* + * struct cmt_platform_data - CMT platform data + * @ape_rst_rq_gpio: GPIO line number for the CMT reset line + */ +struct cmt_platform_data { + unsigned int cmt_rst_ind_gpio; +}; + +struct cmt_device *cmt_get(const char *name); +void cmt_put(struct cmt_device *cmt); +int cmt_notifier_register(struct cmt_device *cmtdev, + struct notifier_block *nb); +int cmt_notifier_unregister(struct cmt_device *cmtdev, + struct notifier_block *nb); +#endif /* __NOKIA_CMT_H__ */ -- cgit v1.2.3