video: tegra: host: Create aux dev nodes in gk20a driver
Lauri Peltonen [Sat, 4 Jan 2014 16:20:03 +0000 (18:20 +0200)]
Create and handle nvhost-ctrl-gpu, nvhost-dbg-gpu and nvhost-prof-gpu
entirely within the gk20a driver.

Bug 1434573

Change-Id: I11d4e22782c48cc010b5f0047606909cc87f4f6b
Signed-off-by: Lauri Peltonen <lpeltonen@nvidia.com>
Reviewed-on: http://git-master/r/352046
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>

drivers/video/tegra/host/gk20a/ctrl_gk20a.c
drivers/video/tegra/host/gk20a/dbg_gpu_gk20a.c
drivers/video/tegra/host/gk20a/dbg_gpu_gk20a.h
drivers/video/tegra/host/gk20a/gk20a.c
drivers/video/tegra/host/gk20a/gk20a.h
drivers/video/tegra/host/gk20a/platform_gk20a.h
drivers/video/tegra/host/gk20a/platform_gk20a_tegra.c

index a4b1ed8..65e0213 100644 (file)
 
 int gk20a_ctrl_dev_open(struct inode *inode, struct file *filp)
 {
-       struct nvhost_device_data *pdata;
-       struct platform_device *dev;
-       struct nvhost_channel *ch;
+       int err;
+       struct gk20a *g;
 
        nvhost_dbg_fn("");
 
-       pdata = container_of(inode->i_cdev,
-                            struct nvhost_device_data, ctrl_cdev);
-       dev = pdata->pdev;
+       g = container_of(inode->i_cdev,
+                        struct gk20a, ctrl.cdev);
 
-       BUG_ON(dev == NULL);
+       filp->private_data = g->dev;
 
-       filp->private_data = dev;
-
-       ch = nvhost_getchannel(pdata->channel, false);
-       if (!ch) {
+       err = gk20a_platform_getchannel(g->dev);
+       if (err) {
                nvhost_dbg_fn("fail to get channel!");
-               return -ENOMEM;
+               return err;
        }
 
        return 0;
@@ -55,11 +51,10 @@ int gk20a_ctrl_dev_open(struct inode *inode, struct file *filp)
 int gk20a_ctrl_dev_release(struct inode *inode, struct file *filp)
 {
        struct platform_device *dev = filp->private_data;
-       struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
 
        nvhost_dbg_fn("");
 
-       nvhost_putchannel(pdata->channel);
+       gk20a_platform_putchannel(dev);
        return 0;
 }
 
index 445df3f..fa52607 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Tegra GK20A GPU Debugger/Profiler Driver
  *
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -64,7 +64,7 @@ static int alloc_session(struct dbg_session_gk20a **_dbg_s)
 int gk20a_dbg_gpu_do_dev_open(struct inode *inode, struct file *filp, bool is_profiler)
 {
        struct dbg_session_gk20a *dbg_session;
-       struct nvhost_device_data *pdata;
+       struct gk20a *g;
 
        struct platform_device *pdev;
        struct device *dev;
@@ -72,12 +72,12 @@ int gk20a_dbg_gpu_do_dev_open(struct inode *inode, struct file *filp, bool is_pr
        int err;
 
        if (!is_profiler)
-               pdata = container_of(inode->i_cdev,
-                            struct nvhost_device_data, dbg_cdev);
+               g = container_of(inode->i_cdev,
+                                struct gk20a, dbg.cdev);
        else
-               pdata = container_of(inode->i_cdev,
-                                    struct nvhost_device_data, prof_cdev);
-       pdev = pdata->pdev;
+               g = container_of(inode->i_cdev,
+                                struct gk20a, prof.cdev);
+       pdev = g->dev;
        dev  = &pdev->dev;
 
        nvhost_dbg(dbg_fn | dbg_gpu_dbg, "dbg session: %s", dev_name(dev));
@@ -87,10 +87,9 @@ int gk20a_dbg_gpu_do_dev_open(struct inode *inode, struct file *filp, bool is_pr
                return err;
 
        filp->private_data = dbg_session;
-       dbg_session->pdata = pdata;
        dbg_session->pdev  = pdev;
        dbg_session->dev   = dev;
-       dbg_session->g     = get_gk20a(pdev);
+       dbg_session->g     = g;
        dbg_session->is_profiler = is_profiler;
        dbg_session->is_pg_disabled = false;
 
index 0a53dda..9281163 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Tegra GK20A GPU Debugger Driver
  *
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -64,7 +64,6 @@ struct dbg_session_gk20a {
        /* gpu module vagaries */
        struct device             *dev;
        struct platform_device    *pdev;
-       struct nvhost_device_data *pdata;
        struct gk20a              *g;
 
        /* bound hwctx and channel, if any */
index 8e1c99e..7ab95f2 100644 (file)
 
 #include "../../../../../arch/arm/mach-tegra/iomap.h"
 
+#define CLASS_NAME "nvidia-gpu"
+/* TODO: Change to e.g. "nvidia-gpu%s" once we have symlinks in place. */
+#define INTERFACE_NAME "nvhost%s-gpu"
+
+#define GK20A_NUM_CDEVS 3
+
 static inline void set_gk20a(struct platform_device *dev, struct gk20a *gk20a)
 {
        gk20a_get_platform(dev)->g = gk20a;
@@ -71,14 +77,14 @@ static struct resource gk20a_intr = {
        .flags = IORESOURCE_IRQ,
 };
 
-const struct file_operations tegra_gk20a_ctrl_ops = {
+static const struct file_operations gk20a_ctrl_ops = {
        .owner = THIS_MODULE,
        .release = gk20a_ctrl_dev_release,
        .open = gk20a_ctrl_dev_open,
        .unlocked_ioctl = gk20a_ctrl_dev_ioctl,
 };
 
-const struct file_operations tegra_gk20a_dbg_gpu_ops = {
+static const struct file_operations gk20a_dbg_ops = {
        .owner = THIS_MODULE,
        .release        = gk20a_dbg_gpu_dev_release,
        .open           = gk20a_dbg_gpu_dev_open,
@@ -95,7 +101,7 @@ const struct file_operations tegra_gk20a_dbg_gpu_ops = {
  * code does get too tangled trying to handle each in the same path we can
  * separate them cleanly.
  */
-const struct file_operations tegra_gk20a_prof_gpu_ops = {
+static const struct file_operations gk20a_prof_ops = {
        .owner = THIS_MODULE,
        .release        = gk20a_dbg_gpu_dev_release,
        .open           = gk20a_prof_gpu_dev_open,
@@ -981,6 +987,116 @@ static int gk20a_suspend_notifier(struct notifier_block *notifier,
        return NOTIFY_DONE;
 }
 
+static int gk20a_create_device(
+       struct platform_device *pdev, int devno, const char *cdev_name,
+       struct cdev *cdev, struct device **out,
+       const struct file_operations *ops)
+{
+       struct device *dev;
+       int err;
+       struct gk20a *g = get_gk20a(pdev);
+
+       nvhost_dbg_fn("");
+
+       cdev_init(cdev, ops);
+       cdev->owner = THIS_MODULE;
+
+       err = cdev_add(cdev, devno, 1);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "failed to add %s cdev\n", cdev_name);
+               return err;
+       }
+
+       dev = device_create(g->class, NULL, devno, NULL,
+               (pdev->id <= 0) ? INTERFACE_NAME : INTERFACE_NAME ".%d",
+               cdev_name, pdev->id);
+
+       if (IS_ERR(dev)) {
+               err = PTR_ERR(dev);
+               cdev_del(cdev);
+               dev_err(&pdev->dev,
+                       "failed to create %s device for %s\n",
+                       cdev_name, pdev->name);
+               return err;
+       }
+
+       *out = dev;
+       return 0;
+}
+
+static void gk20a_user_deinit(struct platform_device *dev)
+{
+       struct gk20a *g = get_gk20a(dev);
+
+       if (g->ctrl.node) {
+               device_destroy(g->class, g->ctrl.cdev.dev);
+               cdev_del(&g->ctrl.cdev);
+       }
+
+       if (g->dbg.node) {
+               device_destroy(g->class, g->dbg.cdev.dev);
+               cdev_del(&g->dbg.cdev);
+       }
+
+       if (g->prof.node) {
+               device_destroy(g->class, g->prof.cdev.dev);
+               cdev_del(&g->prof.cdev);
+       }
+
+       if (g->cdev_region)
+               unregister_chrdev_region(g->cdev_region, GK20A_NUM_CDEVS);
+
+       if (g->class)
+               class_destroy(g->class);
+}
+
+static int gk20a_user_init(struct platform_device *dev)
+{
+       int err;
+       dev_t devno;
+       struct gk20a *g = get_gk20a(dev);
+
+       g->class = class_create(THIS_MODULE, CLASS_NAME);
+       if (IS_ERR(g->class)) {
+               err = PTR_ERR(g->class);
+               g->class = NULL;
+               dev_err(&dev->dev,
+                       "failed to create " CLASS_NAME " class\n");
+               goto fail;
+       }
+
+       err = alloc_chrdev_region(&devno, 0, GK20A_NUM_CDEVS, CLASS_NAME);
+       if (err) {
+               dev_err(&dev->dev, "failed to allocate devno\n");
+               goto fail;
+       }
+       g->cdev_region = devno;
+
+       err = gk20a_create_device(dev, devno++, "-ctrl",
+                                 &g->ctrl.cdev, &g->ctrl.node,
+                                 &gk20a_ctrl_ops);
+       if (err)
+               goto fail;
+
+       err = gk20a_create_device(dev, devno++, "-dbg",
+                                 &g->dbg.cdev, &g->dbg.node,
+                                 &gk20a_dbg_ops);
+       if (err)
+               goto fail;
+
+       err = gk20a_create_device(dev, devno++, "-prof",
+                                 &g->prof.cdev, &g->prof.node,
+                                 &gk20a_prof_ops);
+       if (err)
+               goto fail;
+
+       return 0;
+fail:
+       gk20a_user_deinit(dev);
+       return err;
+}
+
 static int gk20a_probe(struct platform_device *dev)
 {
        struct gk20a *gk20a;
@@ -1018,6 +1134,10 @@ static int gk20a_probe(struct platform_device *dev)
        gk20a->host = nvhost_get_host(dev);
 #endif
 
+       err = gk20a_user_init(dev);
+       if (err)
+               return err;
+
        gk20a_init_support(dev);
 
        /* Initialize the platform interface. */
@@ -1103,6 +1223,8 @@ static int __exit gk20a_remove(struct platform_device *dev)
        if (g->remove_support)
                g->remove_support(dev);
 
+       gk20a_user_deinit(dev);
+
        set_gk20a(dev, 0);
 #ifdef CONFIG_DEBUG_FS
        debugfs_remove(g->debugfs_ltc_enabled);
index 8b2b860..c92072e 100644 (file)
@@ -114,6 +114,24 @@ struct gk20a {
        spinlock_t mc_enable_lock;
 
        struct nvhost_gpu_characteristics gpu_characteristics;
+
+       struct {
+               struct cdev cdev;
+               struct device *node;
+       } ctrl;
+
+       struct {
+               struct cdev cdev;
+               struct device *node;
+       } dbg;
+
+       struct {
+               struct cdev cdev;
+               struct device *node;
+       } prof;
+
+       dev_t cdev_region;
+       struct class *class;
 };
 
 static inline unsigned long gk20a_get_gr_idle_timeout(struct gk20a *g)
@@ -263,10 +281,6 @@ void gk20a_create_sysfs(struct platform_device *dev);
 int clk_gk20a_debugfs_init(struct platform_device *dev);
 #endif
 
-extern const struct file_operations tegra_gk20a_ctrl_ops;
-extern const struct file_operations tegra_gk20a_dbg_gpu_ops;
-extern const struct file_operations tegra_gk20a_prof_gpu_ops;
-
 struct nvhost_hwctx_handler *nvhost_gk20a_alloc_hwctx_handler(u32 syncpt,
                u32 waitbase, struct nvhost_channel *ch);
 
index 3679ee7..fc46e3c 100644 (file)
@@ -50,6 +50,10 @@ struct gk20a_platform {
         *     structure can be obtained by calling gk20a_get_platform).
         */
        int (*probe)(struct platform_device *dev);
+
+       /* TODO(lpeltonen): Can we get rid of these? */
+       int (*getchannel)(struct platform_device *dev);
+       void (*putchannel)(struct platform_device *dev);
 };
 
 static inline struct gk20a_platform *gk20a_get_platform(
@@ -58,6 +62,19 @@ static inline struct gk20a_platform *gk20a_get_platform(
        return (struct gk20a_platform *)platform_get_drvdata(dev);
 }
 
+static inline int gk20a_platform_getchannel(struct platform_device *dev)
+{
+       struct gk20a_platform *p = gk20a_get_platform(dev);
+       if (p->getchannel)
+               return p->getchannel(dev);
+       return 0;
+}
+static inline void gk20a_platform_putchannel(struct platform_device *dev)
+{
+       struct gk20a_platform *p = gk20a_get_platform(dev);
+       if (p->putchannel)
+               p->putchannel(dev);
+}
 extern struct gk20a_platform gk20a_generic_platform;
 #ifdef CONFIG_TEGRA_GK20A
 extern struct gk20a_platform gk20a_tegra_platform;
index 28b91d5..e80991b 100644 (file)
 #define TEGRA_GK20A_SIM_BASE 0x538F0000 /*tbd: get from iomap.h */
 #define TEGRA_GK20A_SIM_SIZE 0x1000     /*tbd: this is a high-side guess */
 
+static int gk20a_tegra_getchannel(struct platform_device *dev)
+{
+       struct gk20a_platform *platform = gk20a_get_platform(dev);
+       return nvhost_getchannel(platform->nvhost.channel, false) == NULL
+               ? -ENOMEM : 0;
+}
+static void gk20a_tegra_putchannel(struct platform_device *dev)
+{
+       struct gk20a_platform *platform = gk20a_get_platform(dev);
+       nvhost_putchannel(platform->nvhost.channel);
+}
+
 static int gk20a_tegra_probe(struct platform_device *dev)
 {
        int err;
@@ -103,9 +115,6 @@ struct gk20a_platform gk20a_tegra_platform = {
                .powergate_delay        = 500,
                .can_powergate          = true,
                .alloc_hwctx_handler    = nvhost_gk20a_alloc_hwctx_handler,
-               .ctrl_ops               = &tegra_gk20a_ctrl_ops,
-               .dbg_ops                = &tegra_gk20a_dbg_gpu_ops,
-               .prof_ops               = &tegra_gk20a_prof_gpu_ops,
                .as_ops                 = &tegra_gk20a_as_ops,
                .moduleid               = NVHOST_MODULE_GPU,
                .init                   = nvhost_gk20a_init,
@@ -125,6 +134,8 @@ struct gk20a_platform gk20a_tegra_platform = {
 #endif
        },
        .probe = gk20a_tegra_probe,
+       .getchannel = gk20a_tegra_getchannel,
+       .putchannel = gk20a_tegra_putchannel,
 };
 
 struct platform_device tegra_gk20a_device = {