diff options
author | Ninad Malwade <nmalwade@nvidia.com> | 2010-02-26 13:54:32 +0530 |
---|---|---|
committer | Ninad Malwade <nmalwade@nvidia.com> | 2010-02-26 14:19:05 +0530 |
commit | 92891e2ab3df0eafc3f064c410d8ac47187ccb07 (patch) | |
tree | af90e4b0c5db5d2d6a461987f7b301a42871b935 /arch/arm | |
parent | 18af2d21101948f32922b18cb9057c9708b288b9 (diff) |
tegra NVEC: Bus driver architecture for nvec drivers
- nvec_user is the bus driver
- tegra-nvec [keyboard], nvec_mouse [touchpad], nvec_battery
are the client drivers for nvec.
- verified: suspend-resume ordering: This is synchronous now.
- Issues: after few suspend-resume EC is failed to suspend.
[working on this issue]
- Bug 648447
- Adding device_unregister for nvec bus driver.
Change-Id: I3c30ceb50a89f67d71050089766897bd7691848d
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/include/nvec_device.h | 81 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvec_user.c | 648 |
2 files changed, 458 insertions, 271 deletions
diff --git a/arch/arm/mach-tegra/include/nvec_device.h b/arch/arm/mach-tegra/include/nvec_device.h new file mode 100644 index 000000000000..71c1f761906b --- /dev/null +++ b/arch/arm/mach-tegra/include/nvec_device.h @@ -0,0 +1,81 @@ +/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef INCLUDED_NVEC_DEVICE_H
+#define INCLUDED_NVEC_DEVICE_H
+
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#define nvec_get_drvdata(f) dev_get_drvdata(&(f)->dev)
+#define nvec_set_drvdata(f,d) dev_set_drvdata(&(f)->dev, d)
+
+struct nvec_driver;
+
+struct nvec_device {
+ char *name;
+ struct device *parent;
+ struct device dev;
+ struct bus_type *bus; /* type of bus device is on */
+ struct nvec_driver *driver; /* which driver has allocated this
+ device */
+};
+
+extern int nvec_register_device(struct nvec_device *pdev);
+extern void nvec_unregister_device(struct nvec_device *pdev);
+
+/*
+ * NVEC function device driver
+ */
+struct nvec_driver {
+ char *name;
+ struct device_driver driver;
+ struct device dev;
+
+ int (*probe)(struct nvec_device *);
+ void (*remove)(struct nvec_device *);
+
+ int (*suspend)(struct nvec_device *dev, pm_message_t state);
+ int (*resume)(struct nvec_device *dev);
+};
+
+extern int nvec_register_driver(struct nvec_driver *);
+extern void nvec_unregister_driver(struct nvec_driver *);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // INCLUDED_NVEC_DEVICE_H
diff --git a/arch/arm/mach-tegra/nvec_user.c b/arch/arm/mach-tegra/nvec_user.c index b11c00e0c0c4..8ee0240b219a 100644 --- a/arch/arm/mach-tegra/nvec_user.c +++ b/arch/arm/mach-tegra/nvec_user.c @@ -1,271 +1,377 @@ -/* - * arch/arm/mach-tegra/nvec_user.c - * - * User-land access to NvEc embedded controller features - * - * Copyright (c) 2008-2009, NVIDIA Corporation. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/platform_device.h> -#include <linux/miscdevice.h> -#include "linux/nvos_ioctl.h" -#include "nvec.h" -#include "linux/nvec_ioctls.h" -#include "nvreftrack.h" -#include "nvassert.h" - -static NvRtHandle s_RtHandle = NULL; - -NvError NvECPackage_Dispatch(void *InBuffer, NvU32 InSize, void *OutBuffer, - NvU32 OutSize, NvDispatchCtx* Ctx); - -static int nvec_open(struct inode *inode, struct file *file); -static int nvec_close(struct inode *inode, struct file *file); -static long nvec_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); - -int nvec_open(struct inode *inode, struct file *file) -{ - NvRtClientHandle Client; - - if (NvRtRegisterClient(s_RtHandle, &Client) != NvSuccess) - return -ENOMEM; - - file->private_data = (void*)Client; - return 0; -} - -int nvec_close(struct inode *inode, struct file *file) -{ - NvRtClientHandle client = (NvRtClientHandle)file->private_data; - - if (NvRtUnregisterClient(s_RtHandle, client)) { - NvDispatchCtx dctx; - - dctx.Rt = s_RtHandle; - dctx.Client = client; - dctx.PackageIdx = 0; - - // TODO: Enable this code for freeing up leaked handles - #if 0 - for (;;) - { - void* ptr = NvRtFreeObjRef(&dctx, - NvRtObjType_NvEc_NvEcHandle, NULL); - if (!ptr) break; - NVRT_LEAK("NvEc", "NvEcHandle", ptr); - NvEcClose(ptr); - } - #endif - - NvRtUnregisterClient(s_RtHandle, client); - } - return 0; -} - -long nvec_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - NvError err; - NvOsIoctlParams p; - NvU32 size; - NvU32 small_buf[8]; - void *ptr = 0; - long e; - NvBool bAlloc = NV_FALSE; - - switch( cmd ) { - case NvECKernelIoctls_Generic: - { - NvDispatchCtx dctx; - - dctx.Rt = s_RtHandle; - dctx.Client = (NvRtClientHandle)file->private_data; - dctx.PackageIdx = 0; - - err = NvOsCopyIn(&p, (void *)arg, sizeof(p)); - if (err != NvSuccess) { - printk("NvECKernelIoctls_Generic: copy in failed\n"); - goto fail; - } - - size = p.InBufferSize + p.InOutBufferSize + p.OutBufferSize; - if (size <= sizeof(small_buf)) { - ptr = small_buf; - } else { - ptr = NvOsAlloc(size); - if (!ptr) { - printk("NvECKernelIoctls_Generic: alloc err\n"); - goto fail; - } - - bAlloc = NV_TRUE; - } - - err = NvOsCopyIn(ptr, p.pBuffer, p.InBufferSize + - p.InOutBufferSize); - if (err != NvSuccess) { - printk("NvECKernelIoctls_Generic: copy in failure\n"); - goto fail; - } - - err = NvECPackage_Dispatch(ptr, - p.InBufferSize + p.InOutBufferSize, - ((NvU8 *)ptr) + p.InBufferSize, p.InOutBufferSize + - p.OutBufferSize, &dctx); - if (err != NvSuccess) { - printk("NvECKernelIoctls_Generic: dispatch failure\n"); - goto fail; - } - - if (p.InOutBufferSize || p.OutBufferSize) { - err = NvOsCopyOut( - ((NvU8 *)((NvOsIoctlParams *)arg)->pBuffer) + - p.InBufferSize, - ((NvU8 *)ptr) + p.InBufferSize, - p.InOutBufferSize + p.OutBufferSize); - if (err != NvSuccess) { - printk("NvECKernelIoctls_Generic: copyout err\n"); - goto fail; - } - } - - break; - } - default: - printk("unknown ioctl code\n"); - goto fail; - } - e = 0; - goto clean; - -fail: - e = -EINVAL; - -clean: - if (bAlloc) - NvOsFree(ptr); - - return e; -} - -#define DEVICE_NAME "nvec" - -static const struct file_operations nvec_fops = -{ - .owner = THIS_MODULE, - .open = nvec_open, - .release = nvec_close, - .unlocked_ioctl = nvec_unlocked_ioctl, -}; - -static struct miscdevice nvec_dev = -{ - .name = DEVICE_NAME, - .fops = &nvec_fops, - .minor = MISC_DYNAMIC_MINOR, -}; - -static NvEcHandle s_NvEcHandle = NULL; - -static int __init nvec_probe(struct platform_device *pdev) -{ - int e = 0; - NvError status; - NvU32 NumTypes = 1; // TODO: must have NvRtObjType_NvEc_Num instead; - - NV_ASSERT(s_RtHandle == NULL); - - if (NvRtCreate(1, &NumTypes, &s_RtHandle) != NvSuccess) { - printk("nvec NvRtCreate returned error\n"); - return -ENOMEM; - } - - status = NvEcOpen(&s_NvEcHandle, 0); - if (status != NvError_Success) { - printk("nvec NvEcOpen returned 0x%x\n", status); - return -EINVAL; - } - - e = misc_register(&nvec_dev); - if (e < 0) { - if (s_RtHandle) { - NvRtDestroy(s_RtHandle); - s_RtHandle = NULL; - } - printk("nvec failed to open\n"); - } - return e; -} - -static int nvec_remove(struct platform_device *pdev) -{ - NvEcClose(s_NvEcHandle); - misc_deregister( &nvec_dev ); - NvRtDestroy(s_RtHandle); - s_RtHandle = NULL; - return 0; -} - -static int nvec_suspend(struct platform_device *pdev, pm_message_t state) -{ - NvError e = NvEcPowerSuspend(NvEcPowerState_Suspend); - if (e != NvSuccess) - return -1; - - return 0; -} - -static int nvec_resume(struct platform_device *pdev) -{ - NvError e = NvEcPowerResume(); - if (e != NvSuccess) - return -1; - - return 0; -} - -static void nvec_shutdown(struct platform_device *pdev) -{ - NvEcPowerSuspend(NvEcPowerState_PowerDown); -} - -static struct platform_driver tegra_nvec_driver = { - .probe = nvec_probe, - .remove = nvec_remove, - .suspend = nvec_suspend, - .resume = nvec_resume, - .shutdown = nvec_shutdown, - .driver = { - .name = "nvec", - }, -}; - -static int __devinit nvec_init( void ) -{ - return platform_driver_register(&tegra_nvec_driver); -} - -static void __exit nvec_deinit( void ) -{ - return platform_driver_unregister(&tegra_nvec_driver); -} - -module_init(nvec_init); -module_exit(nvec_deinit); +/*
+ * arch/arm/mach-tegra/nvec_user.c
+ *
+ * User-land access to NvEc embedded controller features
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include "linux/nvos_ioctl.h"
+#include "nvec.h"
+#include "linux/nvec_ioctls.h"
+#include "nvreftrack.h"
+#include "nvassert.h"
+#include "nvec_device.h"
+
+static NvRtHandle s_RtHandle = NULL;
+int device_count = 0;
+
+#define dev_to_nvec_driver(d) container_of(d, struct nvec_driver, driver)
+#define to_nvec_device(x) container_of((x), struct nvec_device, dev)
+
+NvError NvECPackage_Dispatch(void *InBuffer, NvU32 InSize, void *OutBuffer,
+ NvU32 OutSize, NvDispatchCtx* Ctx);
+
+static int nvec_open(struct inode *inode, struct file *file);
+static int nvec_close(struct inode *inode, struct file *file);
+static long nvec_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+int nvec_open(struct inode *inode, struct file *file)
+{
+ NvRtClientHandle Client;
+
+ if (NvRtRegisterClient(s_RtHandle, &Client) != NvSuccess)
+ return -ENOMEM;
+
+ file->private_data = (void*)Client;
+ return 0;
+}
+
+int nvec_close(struct inode *inode, struct file *file)
+{
+ NvRtClientHandle client = (NvRtClientHandle)file->private_data;
+
+ if (NvRtUnregisterClient(s_RtHandle, client)) {
+ NvDispatchCtx dctx;
+
+ dctx.Rt = s_RtHandle;
+ dctx.Client = client;
+ dctx.PackageIdx = 0;
+
+ // TODO: Enable this code for freeing up leaked handles
+ #if 0
+ for (;;)
+ {
+ void* ptr = NvRtFreeObjRef(&dctx,
+ NvRtObjType_NvEc_NvEcHandle, NULL);
+ if (!ptr) break;
+ NVRT_LEAK("NvEc", "NvEcHandle", ptr);
+ NvEcClose(ptr);
+ }
+ #endif
+
+ NvRtUnregisterClient(s_RtHandle, client);
+ }
+ return 0;
+}
+
+long nvec_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ NvError err;
+ NvOsIoctlParams p;
+ NvU32 size;
+ NvU32 small_buf[8];
+ void *ptr = 0;
+ long e;
+ NvBool bAlloc = NV_FALSE;
+
+ switch( cmd ) {
+ case NvECKernelIoctls_Generic:
+ {
+ NvDispatchCtx dctx;
+
+ dctx.Rt = s_RtHandle;
+ dctx.Client = (NvRtClientHandle)file->private_data;
+ dctx.PackageIdx = 0;
+
+ err = NvOsCopyIn(&p, (void *)arg, sizeof(p));
+ if (err != NvSuccess) {
+ printk("NvECKernelIoctls_Generic: copy in failed\n");
+ goto fail;
+ }
+
+ size = p.InBufferSize + p.InOutBufferSize + p.OutBufferSize;
+ if (size <= sizeof(small_buf)) {
+ ptr = small_buf;
+ } else {
+ ptr = NvOsAlloc(size);
+ if (!ptr) {
+ printk("NvECKernelIoctls_Generic: alloc err\n");
+ goto fail;
+ }
+
+ bAlloc = NV_TRUE;
+ }
+
+ err = NvOsCopyIn(ptr, p.pBuffer, p.InBufferSize +
+ p.InOutBufferSize);
+ if (err != NvSuccess) {
+ printk("NvECKernelIoctls_Generic: copy in failure\n");
+ goto fail;
+ }
+
+ err = NvECPackage_Dispatch(ptr,
+ p.InBufferSize + p.InOutBufferSize,
+ ((NvU8 *)ptr) + p.InBufferSize, p.InOutBufferSize +
+ p.OutBufferSize, &dctx);
+ if (err != NvSuccess) {
+ printk("NvECKernelIoctls_Generic: dispatch failure\n");
+ goto fail;
+ }
+
+ if (p.InOutBufferSize || p.OutBufferSize) {
+ err = NvOsCopyOut(
+ ((NvU8 *)((NvOsIoctlParams *)arg)->pBuffer) +
+ p.InBufferSize,
+ ((NvU8 *)ptr) + p.InBufferSize,
+ p.InOutBufferSize + p.OutBufferSize);
+ if (err != NvSuccess) {
+ printk("NvECKernelIoctls_Generic: copyout err\n");
+ goto fail;
+ }
+ }
+
+ break;
+ }
+ default:
+ printk("unknown ioctl code\n");
+ goto fail;
+ }
+ e = 0;
+ goto clean;
+
+fail:
+ e = -EINVAL;
+
+clean:
+ if (bAlloc)
+ NvOsFree(ptr);
+
+ return e;
+}
+
+#define DEVICE_NAME "nvec"
+
+static const struct file_operations nvec_fops =
+{
+ .owner = THIS_MODULE,
+ .open = nvec_open,
+ .release = nvec_close,
+ .unlocked_ioctl = nvec_unlocked_ioctl,
+};
+
+static struct miscdevice nvec_dev =
+{
+ .name = DEVICE_NAME,
+ .fops = &nvec_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static NvEcHandle s_NvEcHandle = NULL;
+
+static int nvec_bus_probe(struct device *_dev)
+{
+ struct nvec_driver *drv = dev_to_nvec_driver(_dev->driver);
+ struct nvec_device *dev = to_nvec_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int nvec_bus_remove(struct device *_dev)
+{
+ return 0;
+}
+
+int nvec_bus_match(struct device *_dev, struct device_driver *drv)
+{
+ struct nvec_device *dev = to_nvec_device(_dev);
+
+ return (strcmp(dev->name, drv->name) == 0);
+}
+
+static int nvec_bus_suspend(struct device *_dev, pm_message_t state)
+{
+ struct nvec_driver *drv = dev_to_nvec_driver(_dev->driver);
+ struct nvec_device *dev = to_nvec_device(_dev);
+ NvError e = NvError_Success;
+
+ device_count--;
+ drv->suspend(dev, state);
+
+ if (!device_count)
+ {
+ e = NvEcPowerSuspend(NvEcPowerState_Suspend);
+ if (e != NvSuccess) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int nvec_bus_resume(struct device *_dev)
+{
+ struct nvec_driver *drv = dev_to_nvec_driver(_dev->driver);
+ struct nvec_device *dev = to_nvec_device(_dev);
+
+ if (!device_count)
+ {
+ NvError e = NvEcPowerResume();
+ if (e != NvSuccess) {
+ return -1;
+ }
+ }
+
+ device_count++;
+ drv->resume(dev);
+ return 0;
+}
+
+static void nvec_bus_shutdown(struct device *pdev)
+{
+ NvEcPowerSuspend(NvEcPowerState_PowerDown);
+}
+
+static struct bus_type nvec_bus_type = {
+ .name = DEVICE_NAME,
+ .match = nvec_bus_match,
+ .probe = nvec_bus_probe,
+ .remove = nvec_bus_remove,
+ .suspend = nvec_bus_suspend,
+ .resume = nvec_bus_resume,
+ .shutdown = nvec_bus_shutdown,
+};
+
+static struct device nvec_bus_dev = {
+ .init_name = DEVICE_NAME
+};
+
+int nvec_register_driver(struct nvec_driver *drv)
+{
+ drv->driver.name = drv->name;
+ drv->driver.bus = &nvec_bus_type;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(nvec_register_driver);
+
+void nvec_unregister_driver(struct nvec_driver *drv)
+{
+ drv->driver.bus = &nvec_bus_type;
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(nvec_unregister_driver);
+
+int nvec_register_device(struct nvec_device *pdev)
+{
+ if (!pdev)
+ return -EINVAL;
+
+ if (!pdev->dev.parent)
+ pdev->dev.parent = &nvec_bus_dev;
+
+ pdev->dev.bus = &nvec_bus_type;
+
+ dev_set_name(&pdev->dev, pdev->name);
+
+ device_count++;
+ return device_register(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(nvec_register_device);
+
+void nvec_unregister_device(struct nvec_device *pdev)
+{
+ if (pdev) {
+ device_count--;
+ device_unregister(&pdev->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(nvec_unregister_device);
+
+static int __init nvec_init(void)
+{
+ int err = 0;
+ NvError status = NvSuccess;
+ NvU32 NumTypes = 1; // TODO: must have NvRtObjType_NvEc_Num instead;
+
+ err = device_register(&nvec_bus_dev);
+ if (err)
+ return err;
+
+ err = bus_register(&nvec_bus_type);
+ if (err){
+ device_unregister(&nvec_bus_dev);
+ return err;
+ }
+
+
+ NV_ASSERT(s_RtHandle == NULL);
+
+ if (NvRtCreate(1, &NumTypes, &s_RtHandle) != NvSuccess) {
+ printk("nvec NvRtCreate returned error\n");
+ bus_unregister(&nvec_bus_type);
+ device_unregister(&nvec_bus_dev);
+ return -ENOMEM;
+ }
+
+ status = NvEcOpen(&s_NvEcHandle, 0);
+ if (status != NvError_Success) {
+ printk("nvec NvEcOpen returned 0x%x\n", status);
+ NvRtDestroy(s_RtHandle);
+ s_RtHandle = NULL;
+ bus_unregister(&nvec_bus_type);
+ device_unregister(&nvec_bus_dev);
+ return -EINVAL;
+ }
+
+ err = misc_register(&nvec_dev);
+ if (err < 0) {
+ if (s_RtHandle) {
+ NvEcClose(s_NvEcHandle);
+ s_NvEcHandle = NULL;
+ NvRtDestroy(s_RtHandle);
+ s_RtHandle = NULL;
+ bus_unregister(&nvec_bus_type);
+ device_unregister(&nvec_bus_dev);
+ }
+ printk("nvec failed to open\n");
+ }
+
+ return err;
+}
+
+static void __exit nvec_exit(void)
+{
+ NvEcClose(s_NvEcHandle);
+ s_NvEcHandle = NULL;
+ misc_deregister( &nvec_dev );
+ NvRtDestroy(s_RtHandle);
+ s_RtHandle = NULL;
+
+ bus_unregister(&nvec_bus_type);
+ device_unregister(&nvec_bus_dev);
+}
+
+module_init(nvec_init);
+module_exit(nvec_exit);
+
+MODULE_LICENSE("GPL");
\ No newline at end of file |