Add nvhost, the driver for host1x. This patch adds support for reading and incrementing sync points and dynamic power management. Signed-off-by: Terje Bergstrom <tbergstrom@xxxxxxxxxx> --- drivers/video/Kconfig | 2 + drivers/video/Makefile | 2 + drivers/video/tegra/host/Kconfig | 5 + drivers/video/tegra/host/Makefile | 10 + drivers/video/tegra/host/chip_support.c | 48 ++ drivers/video/tegra/host/chip_support.h | 52 +++ drivers/video/tegra/host/dev.c | 96 ++++ drivers/video/tegra/host/host1x/Makefile | 7 + drivers/video/tegra/host/host1x/host1x.c | 204 +++++++++ drivers/video/tegra/host/host1x/host1x.h | 78 ++++ drivers/video/tegra/host/host1x/host1x01.c | 37 ++ drivers/video/tegra/host/host1x/host1x01.h | 29 ++ .../video/tegra/host/host1x/host1x01_hardware.h | 36 ++ drivers/video/tegra/host/host1x/host1x_syncpt.c | 156 +++++++ drivers/video/tegra/host/host1x/hw_host1x01_sync.h | 398 ++++++++++++++++ drivers/video/tegra/host/nvhost_acm.c | 481 ++++++++++++++++++++ drivers/video/tegra/host/nvhost_acm.h | 45 ++ drivers/video/tegra/host/nvhost_syncpt.c | 333 ++++++++++++++ drivers/video/tegra/host/nvhost_syncpt.h | 136 ++++++ include/linux/nvhost.h | 143 ++++++ 20 files changed, 2298 insertions(+) create mode 100644 drivers/video/tegra/host/Kconfig create mode 100644 drivers/video/tegra/host/Makefile create mode 100644 drivers/video/tegra/host/chip_support.c create mode 100644 drivers/video/tegra/host/chip_support.h create mode 100644 drivers/video/tegra/host/dev.c create mode 100644 drivers/video/tegra/host/host1x/Makefile create mode 100644 drivers/video/tegra/host/host1x/host1x.c create mode 100644 drivers/video/tegra/host/host1x/host1x.h create mode 100644 drivers/video/tegra/host/host1x/host1x01.c create mode 100644 drivers/video/tegra/host/host1x/host1x01.h create mode 100644 drivers/video/tegra/host/host1x/host1x01_hardware.h create mode 100644 drivers/video/tegra/host/host1x/host1x_syncpt.c create mode 100644 drivers/video/tegra/host/host1x/hw_host1x01_sync.h create mode 100644 drivers/video/tegra/host/nvhost_acm.c create mode 100644 drivers/video/tegra/host/nvhost_acm.h create mode 100644 drivers/video/tegra/host/nvhost_syncpt.c create mode 100644 drivers/video/tegra/host/nvhost_syncpt.h create mode 100644 include/linux/nvhost.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index fb9a14e..94c861b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2463,4 +2463,6 @@ config FB_SH_MOBILE_MERAM Up to 4 memory channels can be configured, allowing 4 RGB or 2 YCbCr framebuffers to be configured. +source "drivers/video/tegra/host/Kconfig" + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index b936b00..61a4287 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -17,6 +17,8 @@ obj-y += backlight/ obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ +obj-$(CONFIG_TEGRA_HOST1X) += tegra/host/ + obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o diff --git a/drivers/video/tegra/host/Kconfig b/drivers/video/tegra/host/Kconfig new file mode 100644 index 0000000..ebe9bbc --- /dev/null +++ b/drivers/video/tegra/host/Kconfig @@ -0,0 +1,5 @@ +config TEGRA_HOST1X + tristate "Tegra host1x driver" + help + Driver for the Tegra host1x hardware. + diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile new file mode 100644 index 0000000..3edab4a --- /dev/null +++ b/drivers/video/tegra/host/Makefile @@ -0,0 +1,10 @@ +ccflags-y = -Idrivers/video/tegra/host + +nvhost-objs = \ + nvhost_acm.o \ + nvhost_syncpt.o \ + dev.o \ + chip_support.o + +obj-$(CONFIG_TEGRA_HOST1X) += host1x/ +obj-$(CONFIG_TEGRA_HOST1X) += nvhost.o diff --git a/drivers/video/tegra/host/chip_support.c b/drivers/video/tegra/host/chip_support.c new file mode 100644 index 0000000..5a44147 --- /dev/null +++ b/drivers/video/tegra/host/chip_support.c @@ -0,0 +1,48 @@ +/* + * drivers/video/tegra/host/chip_support.c + * + * Tegra host1x chip support module + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/slab.h> + +#include "chip_support.h" +#include "host1x/host1x01.h" + +struct nvhost_chip_support *nvhost_chip_ops; + +struct nvhost_chip_support *nvhost_get_chip_ops(void) +{ + return nvhost_chip_ops; +} + +int nvhost_init_chip_support(struct nvhost_master *host) +{ + if (nvhost_chip_ops == NULL) { + nvhost_chip_ops = kzalloc(sizeof(*nvhost_chip_ops), GFP_KERNEL); + if (nvhost_chip_ops == NULL) { + pr_err("%s: Cannot allocate nvhost_chip_support\n", + __func__); + return -ENOMEM; + } + } + + nvhost_init_host1x01_support(host, nvhost_chip_ops); + return 0; +} diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h new file mode 100644 index 0000000..acfa2f1 --- /dev/null +++ b/drivers/video/tegra/host/chip_support.h @@ -0,0 +1,52 @@ +/* + * drivers/video/tegra/host/chip_support.h + * + * Tegra host1x chip Support + * + * Copyright (c) 2011-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef _NVHOST_CHIP_SUPPORT_H_ +#define _NVHOST_CHIP_SUPPORT_H_ + +#include <linux/types.h> + +struct output; + +struct nvhost_master; +struct nvhost_syncpt; +struct platform_device; + +struct nvhost_syncpt_ops { + void (*reset)(struct nvhost_syncpt *, u32 id); + void (*reset_wait_base)(struct nvhost_syncpt *, u32 id); + void (*read_wait_base)(struct nvhost_syncpt *, u32 id); + u32 (*update_min)(struct nvhost_syncpt *, u32 id); + void (*cpu_incr)(struct nvhost_syncpt *, u32 id); + void (*debug)(struct nvhost_syncpt *); + const char * (*name)(struct nvhost_syncpt *, u32 id); +}; + +struct nvhost_chip_support { + const char *soc_name; + struct nvhost_syncpt_ops syncpt; +}; + +struct nvhost_chip_support *nvhost_get_chip_ops(void); + +#define syncpt_op() (nvhost_get_chip_ops()->syncpt) + +int nvhost_init_chip_support(struct nvhost_master *host); + +#endif /* _NVHOST_CHIP_SUPPORT_H_ */ diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c new file mode 100644 index 0000000..98c9c9f --- /dev/null +++ b/drivers/video/tegra/host/dev.c @@ -0,0 +1,96 @@ +/* + * drivers/video/tegra/host/dev.c + * + * Tegra host1x driver + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include "host1x/host1x.h" +#include "nvhost_acm.h" + +u32 host1x_syncpt_incr_max(u32 id, u32 incrs) +{ + struct nvhost_syncpt *sp = &nvhost->syncpt; + return nvhost_syncpt_incr_max(sp, id, incrs); +} +EXPORT_SYMBOL(host1x_syncpt_incr_max); + +void host1x_syncpt_incr(u32 id) +{ + struct nvhost_syncpt *sp = &nvhost->syncpt; + nvhost_syncpt_incr(sp, id); +} +EXPORT_SYMBOL(host1x_syncpt_incr); + +u32 host1x_syncpt_read(u32 id) +{ + struct nvhost_syncpt *sp = &nvhost->syncpt; + return nvhost_syncpt_read(sp, id); +} +EXPORT_SYMBOL(host1x_syncpt_read); + +bool host1x_powered(struct platform_device *dev) +{ + bool ret = 0; + + /* get the parent */ + if (dev->dev.parent) { + struct platform_device *pdev; + pdev = to_platform_device(dev->dev.parent); + + ret = nvhost_module_powered(pdev); + } else { + dev_warn(&dev->dev, "Cannot return power state, no parent\n"); + } + + return ret; +} +EXPORT_SYMBOL(host1x_powered); + +void host1x_busy(struct platform_device *dev) +{ + /* get the parent */ + if (dev->dev.parent) { + struct platform_device *pdev; + pdev = to_platform_device(dev->dev.parent); + + nvhost_module_busy(pdev); + } else { + dev_warn(&dev->dev, "Cannot turn on, no parent\n"); + } +} +EXPORT_SYMBOL(host1x_busy); + +void host1x_idle(struct platform_device *dev) +{ + /* get the parent */ + if (dev->dev.parent) { + struct platform_device *pdev; + pdev = to_platform_device(dev->dev.parent); + + nvhost_module_idle(pdev); + } else { + dev_warn(&dev->dev, "Cannot idle, no parent\n"); + } +} +EXPORT_SYMBOL(host1x_idle); + +MODULE_AUTHOR("Terje Bergstrom <tbergstrom@xxxxxxxxxx>"); +MODULE_DESCRIPTION("Host1x driver for Tegra products"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform-nvhost"); diff --git a/drivers/video/tegra/host/host1x/Makefile b/drivers/video/tegra/host/host1x/Makefile new file mode 100644 index 0000000..330d507 --- /dev/null +++ b/drivers/video/tegra/host/host1x/Makefile @@ -0,0 +1,7 @@ +ccflags-y = -Idrivers/video/tegra/host + +nvhost-host1x-objs = \ + host1x.o \ + host1x01.o + +obj-$(CONFIG_TEGRA_HOST1X) += nvhost-host1x.o diff --git a/drivers/video/tegra/host/host1x/host1x.c b/drivers/video/tegra/host/host1x/host1x.c new file mode 100644 index 0000000..77ff00b --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x.c @@ -0,0 +1,204 @@ +/* + * drivers/video/tegra/host/host1x.c + * + * Tegra host1x Driver Entrypoint + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/file.h> +#include <linux/clk.h> +#include <linux/hrtimer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/nvhost.h> + +#include "host1x/host1x.h" +#include "nvhost_acm.h" +#include "chip_support.h" + +#define DRIVER_NAME "tegra-host1x" + +struct nvhost_master *nvhost; + +static void power_on_host(struct platform_device *dev) +{ + struct nvhost_master *host = nvhost_get_private_data(dev); + + nvhost_syncpt_reset(&host->syncpt); +} + +static int power_off_host(struct platform_device *dev) +{ + struct nvhost_master *host = nvhost_get_private_data(dev); + + nvhost_syncpt_save(&host->syncpt); + return 0; +} + +static void nvhost_free_resources(struct nvhost_master *host) +{ +} + +static int __devinit nvhost_alloc_resources(struct nvhost_master *host) +{ + int err; + + err = nvhost_init_chip_support(host); + if (err) + return err; + + return 0; +} + +static int __devinit nvhost_probe(struct platform_device *dev) +{ + struct nvhost_master *host; + struct resource *regs, *intr0, *intr1; + int i, err; + struct nvhost_device_data *pdata = + (struct nvhost_device_data *)dev->dev.platform_data; + + regs = platform_get_resource(dev, IORESOURCE_MEM, 0); + intr0 = platform_get_resource(dev, IORESOURCE_IRQ, 0); + intr1 = platform_get_resource(dev, IORESOURCE_IRQ, 1); + + if (!regs || !intr0 || !intr1) { + dev_err(&dev->dev, "missing required platform resources\n"); + return -ENXIO; + } + + host = devm_kzalloc(&dev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + nvhost = host; + + host->dev = dev; + + /* Copy host1x parameters. The private_data gets replaced + * by nvhost_master later */ + memcpy(&host->info, pdata->private_data, + sizeof(struct host1x_device_info)); + + pdata->finalize_poweron = power_on_host; + pdata->prepare_poweroff = power_off_host; + + pdata->pdev = dev; + + /* set common host1x device data */ + platform_set_drvdata(dev, pdata); + + /* set private host1x device data */ + nvhost_set_private_data(dev, host); + + host->aperture = devm_request_and_ioremap(&dev->dev, regs); + if (!host->aperture) { + dev_err(&dev->dev, "failed to remap host registers\n"); + err = -ENXIO; + goto fail; + } + + err = nvhost_alloc_resources(host); + if (err) { + dev_err(&dev->dev, "failed to init chip support\n"); + goto fail; + } + + err = nvhost_syncpt_init(dev, &host->syncpt); + if (err) + goto fail; + + err = nvhost_module_init(dev); + if (err) + goto fail; + + for (i = 0; i < pdata->num_clks; i++) + clk_prepare_enable(pdata->clk[i]); + nvhost_syncpt_reset(&host->syncpt); + for (i = 0; i < pdata->num_clks; i++) + clk_disable_unprepare(pdata->clk[i]); + + dev_info(&dev->dev, "initialized\n"); + + return 0; + +fail: + nvhost_free_resources(host); + kfree(host); + return err; +} + +static int __exit nvhost_remove(struct platform_device *dev) +{ + struct nvhost_master *host = nvhost_get_private_data(dev); + nvhost_syncpt_deinit(&host->syncpt); + nvhost_module_deinit(dev); + nvhost_free_resources(host); + return 0; +} + +static int nvhost_suspend(struct platform_device *dev, pm_message_t state) +{ + struct nvhost_master *host = nvhost_get_private_data(dev); + int ret = 0; + + ret = nvhost_module_suspend(host->dev); + dev_info(&dev->dev, "suspend status: %d\n", ret); + + return ret; +} + +static int nvhost_resume(struct platform_device *dev) +{ + dev_info(&dev->dev, "resuming\n"); + return 0; +} + +static struct of_device_id host1x_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-host1x", }, + { .compatible = "nvidia,tegra30-host1x", }, + { }, +}; + +static struct platform_driver platform_driver = { + .probe = nvhost_probe, + .remove = __exit_p(nvhost_remove), + .suspend = nvhost_suspend, + .resume = nvhost_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(host1x_match), + }, +}; + +static int __init nvhost_mod_init(void) +{ + return platform_driver_register(&platform_driver); +} + +static void __exit nvhost_mod_exit(void) +{ + platform_driver_unregister(&platform_driver); +} + +module_init(nvhost_mod_init); +module_exit(nvhost_mod_exit); + diff --git a/drivers/video/tegra/host/host1x/host1x.h b/drivers/video/tegra/host/host1x/host1x.h new file mode 100644 index 0000000..76748ac --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x.h @@ -0,0 +1,78 @@ +/* + * drivers/video/tegra/host/host1x/host1x.h + * + * Tegra host1x Driver Entrypoint + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_HOST1X_H +#define __NVHOST_HOST1X_H + +#include <linux/cdev.h> +#include <linux/nvhost.h> + +#include "nvhost_syncpt.h" + +#define TRACE_MAX_LENGTH 128U +#define IFACE_NAME "nvhost" + +struct nvhost_master { + void __iomem *aperture; + void __iomem *sync_aperture; + struct nvhost_syncpt syncpt; + struct platform_device *dev; + struct host1x_device_info info; +}; + +extern struct nvhost_master *nvhost; + +static inline void *nvhost_get_private_data(struct platform_device *_dev) +{ + struct nvhost_device_data *pdata = + (struct nvhost_device_data *)platform_get_drvdata(_dev); + WARN_ON(!pdata); + return (pdata && pdata->private_data) ? pdata->private_data : NULL; +} + +static inline void nvhost_set_private_data(struct platform_device *_dev, + void *priv_data) +{ + struct nvhost_device_data *pdata = + (struct nvhost_device_data *)platform_get_drvdata(_dev); + WARN_ON(!pdata); + if (pdata) + pdata->private_data = priv_data; +} + +static inline +struct nvhost_master *nvhost_get_host(struct platform_device *_dev) +{ + struct platform_device *pdev; + + if (_dev->dev.parent) { + pdev = to_platform_device(_dev->dev.parent); + return nvhost_get_private_data(pdev); + } else + return nvhost_get_private_data(_dev); +} + +static inline +struct platform_device *nvhost_get_parent(struct platform_device *_dev) +{ + return _dev->dev.parent ? to_platform_device(_dev->dev.parent) : NULL; +} + +#endif diff --git a/drivers/video/tegra/host/host1x/host1x01.c b/drivers/video/tegra/host/host1x/host1x01.c new file mode 100644 index 0000000..d53302d --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x01.c @@ -0,0 +1,37 @@ +/* + * drivers/video/tegra/host/host1x01.c + * + * Host1x init for T20 and T30 Architecture Chips + * + * Copyright (c) 2011-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/nvhost.h> + +#include "host1x/host1x01.h" +#include "host1x/host1x.h" +#include "host1x/host1x01_hardware.h" +#include "chip_support.h" + +#include "host1x/host1x_syncpt.c" + +int nvhost_init_host1x01_support(struct nvhost_master *host, + struct nvhost_chip_support *op) +{ + host->sync_aperture = host->aperture + HOST1X_CHANNEL_SYNC_REG_BASE; + op->syncpt = host1x_syncpt_ops; + + return 0; +} diff --git a/drivers/video/tegra/host/host1x/host1x01.h b/drivers/video/tegra/host/host1x/host1x01.h new file mode 100644 index 0000000..91624d66 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x01.h @@ -0,0 +1,29 @@ +/* + * drivers/video/tegra/host/host1x01.h + * + * Host1x init for T20 and T30 Architecture Chips + * + * Copyright (c) 2011-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef NVHOST_HOST1X01_H +#define NVHOST_HOST1X01_H + +struct nvhost_master; +struct nvhost_chip_support; + +int nvhost_init_host1x01_support(struct nvhost_master *, + struct nvhost_chip_support *); + +#endif /* NVHOST_HOST1X01_H_ */ diff --git a/drivers/video/tegra/host/host1x/host1x01_hardware.h b/drivers/video/tegra/host/host1x/host1x01_hardware.h new file mode 100644 index 0000000..0da7e06 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x01_hardware.h @@ -0,0 +1,36 @@ +/* + * drivers/video/tegra/host/host1x/host1x01_hardware.h + * + * Tegra host1x Register Offsets for Tegra20 and Tegra30 + * + * Copyright (c) 2010-2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_HOST1X01_HARDWARE_H +#define __NVHOST_HOST1X01_HARDWARE_H + +#include <linux/types.h> +#include <linux/bitops.h> +#include "hw_host1x01_sync.h" + +/* channel registers */ +#define NV_HOST1X_CHANNEL_MAP_SIZE_BYTES 16384 +#define NV_HOST1X_SYNC_MLOCK_NUM 16 + +/* sync registers */ +#define HOST1X_CHANNEL_SYNC_REG_BASE 0x3000 +#define NV_HOST1X_NB_MLOCKS 16 + +#endif diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c new file mode 100644 index 0000000..57cc1b1 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -0,0 +1,156 @@ +/* + * drivers/video/tegra/host/host1x/host1x_syncpt.c + * + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/io.h> +#include "nvhost_syncpt.h" +#include "nvhost_acm.h" +#include "host1x.h" +#include "chip_support.h" + +/** + * Write the current syncpoint value back to hw. + */ +static void host1x_syncpt_reset(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + int min = nvhost_syncpt_read_min(sp, id); + writel(min, dev->sync_aperture + (host1x_sync_syncpt_0_r() + id * 4)); +} + +/** + * Write the current waitbase value back to hw. + */ +static void host1x_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + writel(sp->base_val[id], + dev->sync_aperture + (host1x_sync_syncpt_base_0_r() + id * 4)); +} + +/** + * Read waitbase value from hw. + */ +static void host1x_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + sp->base_val[id] = readl(dev->sync_aperture + + (host1x_sync_syncpt_base_0_r() + id * 4)); +} + +/** + * Updates the last value read from hardware. + * (was nvhost_syncpt_update_min) + */ +static u32 host1x_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + void __iomem *sync_regs = dev->sync_aperture; + u32 old, live; + + do { + old = nvhost_syncpt_read_min(sp, id); + live = readl(sync_regs + (host1x_sync_syncpt_0_r() + id * 4)); + } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); + + if (!nvhost_syncpt_check_max(sp, id, live)) + dev_err(&syncpt_to_dev(sp)->dev->dev, + "%s failed: id=%u, min=%d, max=%d\n", + __func__, + id, + nvhost_syncpt_read_min(sp, id), + nvhost_syncpt_read_max(sp, id)); + + return live; +} + +/** + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +static void host1x_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + u32 reg_offset = id / 32; + + if (!nvhost_module_powered(dev->dev)) { + dev_err(&syncpt_to_dev(sp)->dev->dev, + "Trying to access host1x when it's off"); + return; + } + + if (!nvhost_syncpt_client_managed(sp, id) + && nvhost_syncpt_min_eq_max(sp, id)) { + dev_err(&syncpt_to_dev(sp)->dev->dev, + "Trying to increment syncpoint id %d beyond max\n", + id); + return; + } + writel(BIT_MASK(id), dev->sync_aperture + + host1x_sync_syncpt_cpu_incr_r() + reg_offset * 4); + wmb(); +} + +static const char *host1x_syncpt_name(struct nvhost_syncpt *sp, u32 id) +{ + struct host1x_device_info *info = &syncpt_to_dev(sp)->info; + const char *name = NULL; + + if (id < info->nb_pts) + name = info->syncpt_names[id]; + + return name ? name : ""; +} + +static void host1x_syncpt_debug(struct nvhost_syncpt *sp) +{ + u32 i; + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { + u32 max = nvhost_syncpt_read_max(sp, i); + u32 min = nvhost_syncpt_update_min(sp, i); + if (!max && !min) + continue; + dev_info(&syncpt_to_dev(sp)->dev->dev, + "id %d (%s) min %d max %d\n", + i, syncpt_op().name(sp, i), + min, max); + + } + + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) { + u32 base_val; + host1x_syncpt_read_wait_base(sp, i); + base_val = sp->base_val[i]; + if (base_val) + dev_info(&syncpt_to_dev(sp)->dev->dev, + "waitbase id %d val %d\n", + i, base_val); + + } +} + +static const struct nvhost_syncpt_ops host1x_syncpt_ops = { + .reset = host1x_syncpt_reset, + .reset_wait_base = host1x_syncpt_reset_wait_base, + .read_wait_base = host1x_syncpt_read_wait_base, + .update_min = host1x_syncpt_update_min, + .cpu_incr = host1x_syncpt_cpu_incr, + .debug = host1x_syncpt_debug, + .name = host1x_syncpt_name, +}; diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_sync.h b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h new file mode 100644 index 0000000..67f0cbf --- /dev/null +++ b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h @@ -0,0 +1,398 @@ +/* + * drivers/video/tegra/host/host1x/hw_host1x_sync_host1x.h + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + * + */ + + /* + * Function naming determines intended use: + * + * <x>_r(void) : Returns the offset for register <x>. + * + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. + * + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. + * + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field <y> of register <x>. This value + * can be |'d with others to produce a full register value for + * register <x>. + * + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This + * value can be ~'d and then &'d to clear the value of field <y> for + * register <x>. + * + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted + * to place it at field <y> of register <x>. This value can be |'d + * with others to produce a full register value for <x>. + * + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register + * <x> value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field <y> of register <x>. + * + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for + * field <y> of register <x>. This value is suitable for direct + * comparison with unshifted values appropriate for use in field <y> + * of register <x>. + */ + +#ifndef __hw_host1x_sync_host1x_h__ +#define __hw_host1x_sync_host1x_h__ +/*This file is autogenerated. Do not edit. */ + +static inline u32 host1x_sync_intmask_r(void) +{ + return 0x4; +} +static inline u32 host1x_sync_intc0mask_r(void) +{ + return 0x8; +} +static inline u32 host1x_sync_hintstatus_r(void) +{ + return 0x20; +} +static inline u32 host1x_sync_hintmask_r(void) +{ + return 0x24; +} +static inline u32 host1x_sync_hintstatus_ext_r(void) +{ + return 0x28; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_s(void) +{ + return 1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_f(u32 v) +{ + return (v & 0x1) << 30; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_m(void) +{ + return 0x1 << 30; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_v(u32 r) +{ + return (r >> 30) & 0x1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_s(void) +{ + return 1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_f(u32 v) +{ + return (v & 0x1) << 31; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_m(void) +{ + return 0x1 << 31; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_v(u32 r) +{ + return (r >> 31) & 0x1; +} +static inline u32 host1x_sync_hintmask_ext_r(void) +{ + return 0x2c; +} +static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(void) +{ + return 0x40; +} +static inline u32 host1x_sync_syncpt_thresh_cpu1_int_status_r(void) +{ + return 0x48; +} +static inline u32 host1x_sync_syncpt_thresh_int_disable_r(void) +{ + return 0x60; +} +static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(void) +{ + return 0x68; +} +static inline u32 host1x_sync_cf0_setup_r(void) +{ + return 0x80; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_f(u32 v) +{ + return (v & 0x1ff) << 16; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_m(void) +{ + return 0x1ff << 16; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_v(u32 r) +{ + return (r >> 16) & 0x1ff; +} +static inline u32 host1x_sync_cmdproc_stop_r(void) +{ + return 0xac; +} +static inline u32 host1x_sync_ch_teardown_r(void) +{ + return 0xb0; +} +static inline u32 host1x_sync_usec_clk_r(void) +{ + return 0x1a4; +} +static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void) +{ + return 0x1a8; +} +static inline u32 host1x_sync_ip_busy_timeout_r(void) +{ + return 0x1bc; +} +static inline u32 host1x_sync_ip_read_timeout_addr_r(void) +{ + return 0x1c0; +} +static inline u32 host1x_sync_ip_write_timeout_addr_r(void) +{ + return 0x1c4; +} +static inline u32 host1x_sync_mlock_0_r(void) +{ + return 0x2c0; +} +static inline u32 host1x_sync_mlock_owner_0_r(void) +{ + return 0x340; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_s(void) +{ + return 4; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_f(u32 v) +{ + return (v & 0xf) << 8; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_m(void) +{ + return 0xf << 8; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_v(u32 r) +{ + return (r >> 8) & 0xf; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_s(void) +{ + return 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_f(u32 v) +{ + return (v & 0x1) << 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_m(void) +{ + return 0x1 << 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_v(u32 r) +{ + return (r >> 1) & 0x1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_s(void) +{ + return 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_f(u32 v) +{ + return (v & 0x1) << 0; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_m(void) +{ + return 0x1 << 0; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_v(u32 r) +{ + return (r >> 0) & 0x1; +} +static inline u32 host1x_sync_syncpt_0_r(void) +{ + return 0x400; +} +static inline u32 host1x_sync_syncpt_int_thresh_0_r(void) +{ + return 0x500; +} +static inline u32 host1x_sync_syncpt_base_0_r(void) +{ + return 0x600; +} +static inline u32 host1x_sync_syncpt_cpu_incr_r(void) +{ + return 0x700; +} +static inline u32 host1x_sync_cbread0_r(void) +{ + return 0x720; +} +static inline u32 host1x_sync_cfpeek_ctrl_r(void) +{ + return 0x74c; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_s(void) +{ + return 3; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_f(u32 v) +{ + return (v & 0x7) << 16; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_m(void) +{ + return 0x7 << 16; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_v(u32 r) +{ + return (r >> 16) & 0x7; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_s(void) +{ + return 1; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_f(u32 v) +{ + return (v & 0x1) << 31; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_m(void) +{ + return 0x1 << 31; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_v(u32 r) +{ + return (r >> 31) & 0x1; +} +static inline u32 host1x_sync_cfpeek_read_r(void) +{ + return 0x750; +} +static inline u32 host1x_sync_cfpeek_ptrs_r(void) +{ + return 0x754; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_f(u32 v) +{ + return (v & 0x1ff) << 16; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_m(void) +{ + return 0x1ff << 16; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r) +{ + return (r >> 16) & 0x1ff; +} +static inline u32 host1x_sync_cbstat_0_r(void) +{ + return 0x758; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_s(void) +{ + return 16; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_f(u32 v) +{ + return (v & 0xffff) << 0; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_m(void) +{ + return 0xffff << 0; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_v(u32 r) +{ + return (r >> 0) & 0xffff; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_s(void) +{ + return 10; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_f(u32 v) +{ + return (v & 0x3ff) << 16; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_m(void) +{ + return 0x3ff << 16; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_v(u32 r) +{ + return (r >> 16) & 0x3ff; +} + +#endif /* __hw_host1x_sync_host1x_h__ */ diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c new file mode 100644 index 0000000..15cf395 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -0,0 +1,481 @@ +/* + * drivers/video/tegra/host/nvhost_acm.c + * + * Tegra host1x Automatic Clock Management + * + * Copyright (c) 2010-2012, 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, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <mach/powergate.h> +#include <mach/clk.h> + +#include "nvhost_acm.h" + +#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ) +#define POWERGATE_DELAY 10 +#define MAX_DEVID_LENGTH 16 + +static void do_powergate_locked(int id) +{ + if (id != -1 && tegra_powergate_is_powered(id)) + tegra_powergate_power_off(id); +} + +static void do_unpowergate_locked(int id) +{ + if (id != -1) + tegra_powergate_power_on(id); +} + +static void to_state_clockgated_locked(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + if (pdata->powerstate == NVHOST_POWER_STATE_RUNNING) { + int i, err; + if (pdata->prepare_clockoff) { + err = pdata->prepare_clockoff(dev); + if (err) { + dev_err(&dev->dev, "error clock gating"); + return; + } + } + + for (i = 0; i < pdata->num_clks; i++) + clk_disable_unprepare(pdata->clk[i]); + if (dev->dev.parent) + nvhost_module_idle(to_platform_device(dev->dev.parent)); + } else if (pdata->powerstate == NVHOST_POWER_STATE_POWERGATED + && pdata->can_powergate) { + do_unpowergate_locked(pdata->powergate_ids[0]); + do_unpowergate_locked(pdata->powergate_ids[1]); + } + pdata->powerstate = NVHOST_POWER_STATE_CLOCKGATED; +} + +static void to_state_running_locked(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + int prev_state = pdata->powerstate; + + if (pdata->powerstate == NVHOST_POWER_STATE_POWERGATED) + to_state_clockgated_locked(dev); + + if (pdata->powerstate == NVHOST_POWER_STATE_CLOCKGATED) { + int i; + + if (dev->dev.parent) + nvhost_module_busy(to_platform_device(dev->dev.parent)); + + for (i = 0; i < pdata->num_clks; i++) { + int err = clk_prepare_enable(pdata->clk[i]); + if (err) { + dev_err(&dev->dev, "Cannot turn on clock %s", + pdata->clocks[i].name); + return; + } + } + + if (pdata->finalize_clockon) + pdata->finalize_clockon(dev); + + /* Invoke callback after power un-gating. This is used for + * restoring context. */ + if (prev_state == NVHOST_POWER_STATE_POWERGATED + && pdata->finalize_poweron) + pdata->finalize_poweron(dev); + } + pdata->powerstate = NVHOST_POWER_STATE_RUNNING; +} + +/* This gets called from powergate_handler() and from module suspend. + * Module suspend is done for all modules, runtime power gating only + * for modules with can_powergate set. + */ +static int to_state_powergated_locked(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + int err = 0; + + if (pdata->prepare_poweroff && + pdata->powerstate != NVHOST_POWER_STATE_POWERGATED) { + /* Clock needs to be on in prepare_poweroff */ + to_state_running_locked(dev); + err = pdata->prepare_poweroff(dev); + if (err) + return err; + } + + if (pdata->powerstate == NVHOST_POWER_STATE_RUNNING) + to_state_clockgated_locked(dev); + + if (pdata->can_powergate) { + do_powergate_locked(pdata->powergate_ids[0]); + do_powergate_locked(pdata->powergate_ids[1]); + } + + pdata->powerstate = NVHOST_POWER_STATE_POWERGATED; + return 0; +} + +static void schedule_powergating_locked(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + if (pdata->can_powergate) + schedule_delayed_work(&pdata->powerstate_down, + msecs_to_jiffies(pdata->powergate_delay)); +} + +static void schedule_clockgating_locked(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + schedule_delayed_work(&pdata->powerstate_down, + msecs_to_jiffies(pdata->clockgate_delay)); +} + +void nvhost_module_busy(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + if (pdata->busy) + pdata->busy(dev); + + mutex_lock(&pdata->lock); + cancel_delayed_work(&pdata->powerstate_down); + + pdata->refcount++; + if (pdata->refcount > 0 && !nvhost_module_powered(dev)) + to_state_running_locked(dev); + mutex_unlock(&pdata->lock); +} + +static void powerstate_down_handler(struct work_struct *work) +{ + struct platform_device *dev; + struct nvhost_device_data *pdata; + + pdata = container_of(to_delayed_work(work), + struct nvhost_device_data, + powerstate_down); + + dev = pdata->pdev; + + mutex_lock(&pdata->lock); + if (pdata->refcount == 0) { + switch (pdata->powerstate) { + case NVHOST_POWER_STATE_RUNNING: + to_state_clockgated_locked(dev); + schedule_powergating_locked(dev); + break; + case NVHOST_POWER_STATE_CLOCKGATED: + if (to_state_powergated_locked(dev)) + schedule_powergating_locked(dev); + break; + default: + break; + } + } + mutex_unlock(&pdata->lock); +} + +void nvhost_module_idle_mult(struct platform_device *dev, int refs) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + bool kick = false; + + mutex_lock(&pdata->lock); + pdata->refcount -= refs; + if (pdata->refcount == 0) { + if (nvhost_module_powered(dev)) + schedule_clockgating_locked(dev); + kick = true; + } + mutex_unlock(&pdata->lock); + + if (kick) { + wake_up(&pdata->idle_wq); + + if (pdata->idle) + pdata->idle(dev); + } +} + +static ssize_t refcount_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, + power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]); + struct platform_device *dev = power_attribute->ndev; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + mutex_lock(&pdata->lock); + ret = sprintf(buf, "%d\n", pdata->refcount); + mutex_unlock(&pdata->lock); + + return ret; +} + +static ssize_t powergate_delay_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int powergate_delay = 0, ret = 0; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); + struct platform_device *dev = power_attribute->ndev; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + if (!pdata->can_powergate) { + dev_info(&dev->dev, "does not support power-gating\n"); + return count; + } + + mutex_lock(&pdata->lock); + ret = sscanf(buf, "%d", &powergate_delay); + if (ret == 1 && powergate_delay >= 0) + pdata->powergate_delay = powergate_delay; + else + dev_err(&dev->dev, "Invalid powergate delay\n"); + mutex_unlock(&pdata->lock); + + return count; +} + +static ssize_t powergate_delay_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); + struct platform_device *dev = power_attribute->ndev; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + mutex_lock(&pdata->lock); + ret = sprintf(buf, "%d\n", pdata->powergate_delay); + mutex_unlock(&pdata->lock); + + return ret; +} + +static ssize_t clockgate_delay_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int clockgate_delay = 0, ret = 0; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); + struct platform_device *dev = power_attribute->ndev; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + mutex_lock(&pdata->lock); + ret = sscanf(buf, "%d", &clockgate_delay); + if (ret == 1 && clockgate_delay >= 0) + pdata->clockgate_delay = clockgate_delay; + else + dev_err(&dev->dev, "Invalid clockgate delay\n"); + mutex_unlock(&pdata->lock); + + return count; +} + +static ssize_t clockgate_delay_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); + struct platform_device *dev = power_attribute->ndev; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + mutex_lock(&pdata->lock); + ret = sprintf(buf, "%d\n", pdata->clockgate_delay); + mutex_unlock(&pdata->lock); + + return ret; +} + +int nvhost_module_init(struct platform_device *dev) +{ + int i = 0, err = 0; + struct kobj_attribute *attr = NULL; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + /* initialize clocks to known state */ + while (pdata->clocks[i].name && i < NVHOST_MODULE_MAX_CLOCKS) { + long rate = pdata->clocks[i].default_rate; + struct clk *c; + + c = devm_clk_get(&dev->dev, pdata->clocks[i].name); + if (IS_ERR_OR_NULL(c)) { + dev_err(&dev->dev, "Cannot get clock %s\n", + pdata->clocks[i].name); + return -ENODEV; + } + + rate = clk_round_rate(c, rate); + clk_prepare_enable(c); + clk_set_rate(c, rate); + clk_disable_unprepare(c); + pdata->clk[i] = c; + i++; + } + pdata->num_clks = i; + + mutex_init(&pdata->lock); + init_waitqueue_head(&pdata->idle_wq); + INIT_DELAYED_WORK(&pdata->powerstate_down, powerstate_down_handler); + + /* power gate units that we can power gate */ + if (pdata->can_powergate) { + do_powergate_locked(pdata->powergate_ids[0]); + do_powergate_locked(pdata->powergate_ids[1]); + pdata->powerstate = NVHOST_POWER_STATE_POWERGATED; + } else { + do_unpowergate_locked(pdata->powergate_ids[0]); + do_unpowergate_locked(pdata->powergate_ids[1]); + pdata->powerstate = NVHOST_POWER_STATE_CLOCKGATED; + } + + /* Init the power sysfs attributes for this device */ + pdata->power_attrib = devm_kzalloc(&dev->dev, + sizeof(struct nvhost_device_power_attr), + GFP_KERNEL); + if (!pdata->power_attrib) { + dev_err(&dev->dev, "Unable to allocate sysfs attributes\n"); + return -ENOMEM; + } + pdata->power_attrib->ndev = dev; + + pdata->power_kobj = kobject_create_and_add("acm", &dev->dev.kobj); + if (!pdata->power_kobj) { + dev_err(&dev->dev, "Could not add dir 'power'\n"); + err = -EIO; + goto fail_attrib_alloc; + } + + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; + attr->attr.name = "clockgate_delay"; + attr->attr.mode = S_IWUSR | S_IRUGO; + attr->show = clockgate_delay_show; + attr->store = clockgate_delay_store; + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute clockgate_delay\n"); + err = -EIO; + goto fail_clockdelay; + } + + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; + attr->attr.name = "powergate_delay"; + attr->attr.mode = S_IWUSR | S_IRUGO; + attr->show = powergate_delay_show; + attr->store = powergate_delay_store; + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute powergate_delay\n"); + err = -EIO; + goto fail_powergatedelay; + } + + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]; + attr->attr.name = "refcount"; + attr->attr.mode = S_IRUGO; + attr->show = refcount_show; + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute refcount\n"); + err = -EIO; + goto fail_refcount; + } + + return 0; + +fail_refcount: + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; + sysfs_remove_file(pdata->power_kobj, &attr->attr); + +fail_powergatedelay: + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; + sysfs_remove_file(pdata->power_kobj, &attr->attr); + +fail_clockdelay: + kobject_put(pdata->power_kobj); + +fail_attrib_alloc: + kfree(pdata->power_attrib); + + return err; +} + +static int is_module_idle(struct platform_device *dev) +{ + int count; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + mutex_lock(&pdata->lock); + count = pdata->refcount; + mutex_unlock(&pdata->lock); + + return (count == 0); +} + +int nvhost_module_suspend(struct platform_device *dev) +{ + int ret; + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + ret = wait_event_timeout(pdata->idle_wq, is_module_idle(dev), + ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT); + if (ret == 0) { + dev_info(&dev->dev, "%s prevented suspend\n", + dev_name(&dev->dev)); + return -EBUSY; + } + + mutex_lock(&pdata->lock); + cancel_delayed_work(&pdata->powerstate_down); + to_state_powergated_locked(dev); + mutex_unlock(&pdata->lock); + + if (pdata->suspend_ndev) + pdata->suspend_ndev(dev); + + return 0; +} + +void nvhost_module_deinit(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + + kobject_put(pdata->power_kobj); + + if (pdata->deinit) + pdata->deinit(dev); + + nvhost_module_suspend(dev); + pdata->powerstate = NVHOST_POWER_STATE_DEINIT; +} diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h new file mode 100644 index 0000000..0892a57 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_acm.h @@ -0,0 +1,45 @@ +/* + * drivers/video/tegra/host/nvhost_acm.h + * + * Tegra host1x Automatic Clock Management + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_ACM_H +#define __NVHOST_ACM_H + +#include <linux/nvhost.h> + +/* Sets clocks and powergating state for a module */ +int nvhost_module_init(struct platform_device *ndev); +void nvhost_module_deinit(struct platform_device *dev); +int nvhost_module_suspend(struct platform_device *dev); + +void nvhost_module_busy(struct platform_device *dev); +void nvhost_module_idle_mult(struct platform_device *dev, int refs); + +static inline bool nvhost_module_powered(struct platform_device *dev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(dev); + return pdata->powerstate == NVHOST_POWER_STATE_RUNNING; +} + +static inline void nvhost_module_idle(struct platform_device *dev) +{ + nvhost_module_idle_mult(dev, 1); +} + +#endif diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c new file mode 100644 index 0000000..d7c8230 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -0,0 +1,333 @@ +/* + * drivers/video/tegra/host/nvhost_syncpt.c + * + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include "nvhost_syncpt.h" +#include "nvhost_acm.h" +#include "host1x/host1x.h" +#include "chip_support.h" + +#define MAX_SYNCPT_LENGTH 5 + +/* Name of sysfs node for min and max value */ +static const char *min_name = "min"; +static const char *max_name = "max"; + +/** + * Resets syncpoint and waitbase values to sw shadows + */ +void nvhost_syncpt_reset(struct nvhost_syncpt *sp) +{ + u32 i; + + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) + syncpt_op().reset(sp, i); + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) + syncpt_op().reset_wait_base(sp, i); + wmb(); +} + +/** + * Updates sw shadow state for client managed registers + */ +void nvhost_syncpt_save(struct nvhost_syncpt *sp) +{ + u32 i; + + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { + if (nvhost_syncpt_client_managed(sp, i)) + syncpt_op().update_min(sp, i); + else + WARN_ON(!nvhost_syncpt_min_eq_max(sp, i)); + } + + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) + syncpt_op().read_wait_base(sp, i); +} + +/** + * Updates the last value read from hardware. + */ +u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) +{ + u32 val; + + val = syncpt_op().update_min(sp, id); + + return val; +} + +/** + * Get the current syncpoint value + */ +u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) +{ + u32 val; + nvhost_module_busy(syncpt_to_dev(sp)->dev); + val = syncpt_op().update_min(sp, id); + nvhost_module_idle(syncpt_to_dev(sp)->dev); + return val; +} + +/** + * Get the current syncpoint base + */ +u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + u32 val; + nvhost_module_busy(syncpt_to_dev(sp)->dev); + syncpt_op().read_wait_base(sp, id); + val = sp->base_val[id]; + nvhost_module_idle(syncpt_to_dev(sp)->dev); + return val; +} + +/** + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) +{ + syncpt_op().cpu_incr(sp, id); +} + +/** + * Increment syncpoint value from cpu, updating cache + */ +void nvhost_syncpt_incr(struct nvhost_syncpt *sp, u32 id) +{ + if (nvhost_syncpt_client_managed(sp, id)) + nvhost_syncpt_incr_max(sp, id, 1); + nvhost_module_busy(syncpt_to_dev(sp)->dev); + nvhost_syncpt_cpu_incr(sp, id); + nvhost_module_idle(syncpt_to_dev(sp)->dev); +} + +/** + * Returns true if syncpoint is expired, false if we may need to wait + */ +bool nvhost_syncpt_is_expired( + struct nvhost_syncpt *sp, + u32 id, + u32 thresh) +{ + u32 current_val; + u32 future_val; + smp_rmb(); + current_val = (u32)atomic_read(&sp->min_val[id]); + future_val = (u32)atomic_read(&sp->max_val[id]); + + /* Note the use of unsigned arithmetic here (mod 1<<32). + * + * c = current_val = min_val = the current value of the syncpoint. + * t = thresh = the value we are checking + * f = future_val = max_val = the value c will reach when all + * outstanding increments have completed. + * + * Note that c always chases f until it reaches f. + * + * Dtf = (f - t) + * Dtc = (c - t) + * + * Consider all cases: + * + * A) .....c..t..f..... Dtf < Dtc need to wait + * B) .....c.....f..t.. Dtf > Dtc expired + * C) ..t..c.....f..... Dtf > Dtc expired (Dct very large) + * + * Any case where f==c: always expired (for any t). Dtf == Dcf + * Any case where t==c: always expired (for any f). Dtf >= Dtc (because Dtc==0) + * Any case where t==f!=c: always wait. Dtf < Dtc (because Dtf==0, + * Dtc!=0) + * + * Other cases: + * + * A) .....t..f..c..... Dtf < Dtc need to wait + * A) .....f..c..t..... Dtf < Dtc need to wait + * A) .....f..t..c..... Dtf > Dtc expired + * + * So: + * Dtf >= Dtc implies EXPIRED (return true) + * Dtf < Dtc implies WAIT (return false) + * + * Note: If t is expired then we *cannot* wait on it. We would wait + * forever (hang the system). + * + * Note: do NOT get clever and remove the -thresh from both sides. It + * is NOT the same. + * + * If future valueis zero, we have a client managed sync point. In that + * case we do a direct comparison. + */ + if (!nvhost_syncpt_client_managed(sp, id)) + return future_val - thresh >= current_val - thresh; + else + return (s32)(current_val - thresh) >= 0; +} + +void nvhost_syncpt_debug(struct nvhost_syncpt *sp) +{ + syncpt_op().debug(sp); +} +/* Displays the current value of the sync point via sysfs */ +static ssize_t syncpt_min_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct nvhost_syncpt_attr *syncpt_attr = + container_of(attr, struct nvhost_syncpt_attr, attr); + + return snprintf(buf, PAGE_SIZE, "%u", + nvhost_syncpt_read(&syncpt_attr->host->syncpt, + syncpt_attr->id)); +} + +static ssize_t syncpt_max_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct nvhost_syncpt_attr *syncpt_attr = + container_of(attr, struct nvhost_syncpt_attr, attr); + + return snprintf(buf, PAGE_SIZE, "%u", + nvhost_syncpt_read_max(&syncpt_attr->host->syncpt, + syncpt_attr->id)); +} + +int nvhost_syncpt_init(struct platform_device *dev, + struct nvhost_syncpt *sp) +{ + int i; + struct nvhost_master *host = syncpt_to_dev(sp); + int err = 0; + + /* Allocate structs for min, max and base values */ + sp->min_val = kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_pts(sp), + GFP_KERNEL); + sp->max_val = kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_pts(sp), + GFP_KERNEL); + sp->base_val = kzalloc(sizeof(u32) * nvhost_syncpt_nb_bases(sp), + GFP_KERNEL); + sp->lock_counts = + kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_mlocks(sp), + GFP_KERNEL); + + if (!(sp->min_val && sp->max_val && sp->base_val && sp->lock_counts)) { + /* frees happen in the deinit */ + err = -ENOMEM; + goto fail; + } + + sp->kobj = kobject_create_and_add("syncpt", &dev->dev.kobj); + if (!sp->kobj) { + err = -EIO; + goto fail; + } + + /* Allocate two attributes for each sync point: min and max */ + sp->syncpt_attrs = kzalloc(sizeof(*sp->syncpt_attrs) + * nvhost_syncpt_nb_pts(sp) * 2, GFP_KERNEL); + if (!sp->syncpt_attrs) { + err = -ENOMEM; + goto fail; + } + + /* Fill in the attributes */ + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { + char name[MAX_SYNCPT_LENGTH]; + struct kobject *kobj; + struct nvhost_syncpt_attr *min = &sp->syncpt_attrs[i*2]; + struct nvhost_syncpt_attr *max = &sp->syncpt_attrs[i*2+1]; + + /* Create one directory per sync point */ + snprintf(name, sizeof(name), "%d", i); + kobj = kobject_create_and_add(name, sp->kobj); + if (!kobj) { + err = -EIO; + goto fail; + } + + min->id = i; + min->host = host; + min->attr.attr.name = min_name; + min->attr.attr.mode = S_IRUGO; + min->attr.show = syncpt_min_show; + if (sysfs_create_file(kobj, &min->attr.attr)) { + err = -EIO; + goto fail; + } + + max->id = i; + max->host = host; + max->attr.attr.name = max_name; + max->attr.attr.mode = S_IRUGO; + max->attr.show = syncpt_max_show; + if (sysfs_create_file(kobj, &max->attr.attr)) { + err = -EIO; + goto fail; + } + } + + return err; + +fail: + nvhost_syncpt_deinit(sp); + return err; +} + +void nvhost_syncpt_deinit(struct nvhost_syncpt *sp) +{ + kobject_put(sp->kobj); + + kfree(sp->min_val); + sp->min_val = NULL; + + kfree(sp->max_val); + sp->max_val = NULL; + + kfree(sp->base_val); + sp->base_val = NULL; + + kfree(sp->lock_counts); + sp->lock_counts = 0; + + kfree(sp->syncpt_attrs); + sp->syncpt_attrs = NULL; +} + +int nvhost_syncpt_client_managed(struct nvhost_syncpt *sp, u32 id) +{ + return BIT(id) & syncpt_to_dev(sp)->info.client_managed; +} + +int nvhost_syncpt_nb_pts(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_pts; +} + +int nvhost_syncpt_nb_bases(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_bases; +} + +int nvhost_syncpt_nb_mlocks(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_mlocks; +} diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h new file mode 100644 index 0000000..b883442 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -0,0 +1,136 @@ +/* + * drivers/video/tegra/host/nvhost_syncpt.h + * + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_SYNCPT_H +#define __NVHOST_SYNCPT_H + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/nvhost.h> +#include <linux/atomic.h> + +/* host managed and invalid syncpt id */ +#define NVSYNCPT_GRAPHICS_HOST (0) + +/* Attribute struct for sysfs min and max attributes */ +struct nvhost_syncpt_attr { + struct kobj_attribute attr; + struct nvhost_master *host; + int id; +}; + +struct nvhost_syncpt { + struct kobject *kobj; + atomic_t *min_val; + atomic_t *max_val; + u32 *base_val; + atomic_t *lock_counts; + const char **syncpt_names; + struct nvhost_syncpt_attr *syncpt_attrs; +}; + +int nvhost_syncpt_init(struct platform_device *, struct nvhost_syncpt *); +void nvhost_syncpt_deinit(struct nvhost_syncpt *); + +#define syncpt_to_dev(sp) container_of(sp, struct nvhost_master, syncpt) +#define SYNCPT_CHECK_PERIOD (2 * HZ) +#define MAX_STUCK_CHECK_COUNT 15 + +/** + * Updates the value sent to hardware. + */ +static inline u32 nvhost_syncpt_incr_max(struct nvhost_syncpt *sp, + u32 id, u32 incrs) +{ + return (u32)atomic_add_return(incrs, &sp->max_val[id]); +} + +/** + * Updated the value sent to hardware. + */ +static inline u32 nvhost_syncpt_set_max(struct nvhost_syncpt *sp, + u32 id, u32 val) +{ + atomic_set(&sp->max_val[id], val); + smp_wmb(); + return val; +} + +static inline u32 nvhost_syncpt_read_max(struct nvhost_syncpt *sp, u32 id) +{ + smp_rmb(); + return (u32)atomic_read(&sp->max_val[id]); +} + +static inline u32 nvhost_syncpt_read_min(struct nvhost_syncpt *sp, u32 id) +{ + smp_rmb(); + return (u32)atomic_read(&sp->min_val[id]); +} + +int nvhost_syncpt_client_managed(struct nvhost_syncpt *sp, u32 id); +int nvhost_syncpt_nb_pts(struct nvhost_syncpt *sp); +int nvhost_syncpt_nb_bases(struct nvhost_syncpt *sp); +int nvhost_syncpt_nb_mlocks(struct nvhost_syncpt *sp); + +static inline bool nvhost_syncpt_check_max(struct nvhost_syncpt *sp, + u32 id, u32 real) +{ + u32 max; + if (nvhost_syncpt_client_managed(sp, id)) + return true; + max = nvhost_syncpt_read_max(sp, id); + return (s32)(max - real) >= 0; +} + +/** + * Returns true if syncpoint min == max + */ +static inline bool nvhost_syncpt_min_eq_max(struct nvhost_syncpt *sp, u32 id) +{ + int min, max; + smp_rmb(); + min = atomic_read(&sp->min_val[id]); + max = atomic_read(&sp->max_val[id]); + return (min == max); +} + +void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id); + +u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id); +bool nvhost_syncpt_is_expired(struct nvhost_syncpt *sp, u32 id, u32 thresh); + +void nvhost_syncpt_save(struct nvhost_syncpt *sp); + +void nvhost_syncpt_reset(struct nvhost_syncpt *sp); + +u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id); +u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id); + +void nvhost_syncpt_incr(struct nvhost_syncpt *sp, u32 id); + +void nvhost_syncpt_debug(struct nvhost_syncpt *sp); + +static inline int nvhost_syncpt_is_valid(struct nvhost_syncpt *sp, u32 id) +{ + return id != NVSYNCPT_INVALID && id < nvhost_syncpt_nb_pts(sp); +} + +#endif diff --git a/include/linux/nvhost.h b/include/linux/nvhost.h new file mode 100644 index 0000000..20ba2a5 --- /dev/null +++ b/include/linux/nvhost.h @@ -0,0 +1,143 @@ +/* + * include/linux/nvhost.h + * + * Tegra host1x driver + * + * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved. + * + * 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. + */ + +#ifndef __LINUX_NVHOST_H +#define __LINUX_NVHOST_H + +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> + +struct nvhost_device_power_attr; + +#define NVHOST_MODULE_MAX_CLOCKS 3 +#define NVHOST_MODULE_MAX_POWERGATE_IDS 2 +#define NVHOST_MODULE_NO_POWERGATE_IDS .powergate_ids = {-1, -1} +#define NVHOST_DEFAULT_CLOCKGATE_DELAY .clockgate_delay = 25 +#define NVHOST_NAME_SIZE 24 +#define NVSYNCPT_INVALID (-1) + +enum nvhost_power_sysfs_attributes { + NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY = 0, + NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY, + NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT, + NVHOST_POWER_SYSFS_ATTRIB_MAX +}; + +struct nvhost_clock { + char *name; + unsigned long default_rate; + int reset; +}; + +enum nvhost_device_powerstate_t { + NVHOST_POWER_STATE_DEINIT, + NVHOST_POWER_STATE_RUNNING, + NVHOST_POWER_STATE_CLOCKGATED, + NVHOST_POWER_STATE_POWERGATED +}; + +struct host1x_device_info { + int nb_channels; /* host1x: num channels supported */ + int nb_pts; /* host1x: num syncpoints supported */ + int nb_bases; /* host1x: num syncpoints supported */ + u32 client_managed; /* host1x: client managed syncpts */ + int nb_mlocks; /* host1x: number of mlocks */ + const char **syncpt_names; /* names of sync points */ +}; + +struct nvhost_device_data { + int version; /* ip version number of device */ + int id; /* Separates clients of same hw */ + int index; /* Hardware channel number */ + void __iomem *aperture; /* Iomem mapped to kernel */ + + u32 syncpts; /* Bitfield of sync points used */ + u32 modulemutexes; /* Bit field of module mutexes */ + + u32 class; /* Device class */ + bool serialize; /* Serialize submits in the channel */ + + int powergate_ids[NVHOST_MODULE_MAX_POWERGATE_IDS]; + bool can_powergate; /* True if module can be power gated */ + int clockgate_delay;/* Delay before clock gated */ + int powergate_delay;/* Delay before power gated */ + struct nvhost_clock clocks[NVHOST_MODULE_MAX_CLOCKS];/* Clock names */ + + struct delayed_work powerstate_down;/* Power state management */ + int num_clks; /* Number of clocks opened for dev */ + struct clk *clk[NVHOST_MODULE_MAX_CLOCKS]; + struct mutex lock; /* Power management lock */ + int powerstate; /* Current power state */ + int refcount; /* Number of tasks active */ + wait_queue_head_t idle_wq; /* Work queue for idle */ + + struct nvhost_channel *channel; /* Channel assigned for the module */ + struct kobject *power_kobj; /* kobj to hold power sysfs entries */ + struct nvhost_device_power_attr *power_attrib; /* sysfs attributes */ + struct dentry *debugfs; /* debugfs directory */ + + void *private_data; /* private platform data */ + struct platform_device *pdev; /* owner platform_device */ + + /* Finalize power on. Can be used for context restore. */ + void (*finalize_poweron)(struct platform_device *dev); + + /* Device is busy. */ + void (*busy)(struct platform_device *); + + /* Device is idle. */ + void (*idle)(struct platform_device *); + + /* Device is going to be suspended */ + void (*suspend_ndev)(struct platform_device *); + + /* Device is initialized */ + void (*init)(struct platform_device *dev); + + /* Device is de-initialized. */ + void (*deinit)(struct platform_device *dev); + + /* Preparing for power off. Used for context save. */ + int (*prepare_poweroff)(struct platform_device *dev); + + /* Clock gating callbacks */ + int (*prepare_clockoff)(struct platform_device *dev); + void (*finalize_clockon)(struct platform_device *dev); +}; + +struct nvhost_device_power_attr { + struct platform_device *ndev; + struct kobj_attribute power_attr[NVHOST_POWER_SYSFS_ATTRIB_MAX]; +}; + +/* public host1x power management APIs */ +bool host1x_powered(struct platform_device *dev); +void host1x_busy(struct platform_device *dev); +void host1x_idle(struct platform_device *dev); + +/* public host1x sync-point management APIs */ +u32 host1x_syncpt_incr_max(u32 id, u32 incrs); +void host1x_syncpt_incr(u32 id); +u32 host1x_syncpt_read(u32 id); + +#endif -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html