Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd8f3c6d authored by Tianyi Gou's avatar Tianyi Gou
Browse files

msm: pil-vpu: Introduce the PIL VPU driver



Introduce the PIL VPU driver to support the boot and shutdown
of the VPU subsystem.

Change-Id: Idbfbaf2cfac804dda0e853b864459fc729e5db56
Signed-off-by: default avatarTianyi Gou <tgou@codeaurora.org>
parent e1e5c4cc
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1220,6 +1220,16 @@ config MSM_PIL_VENUS
	  Support for booting and shutting down the VENUS processor (Video).
	  Venus is the Video subsystem processor used for video codecs.

config MSM_PIL_VPU
	tristate "VPU Boot Support"
	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
	help
	  Support for booting and shutting down the VPU (Video Processing Unit)
	  processor.

	  VPU is the Video Processing subsystem processor used for
	  video processing.

config MSM_PIL_GSS
	tristate "GSS (Cortex A5) Boot Support"
	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ obj-$(CONFIG_MSM_PIL_DSPS) += pil-dsps.o
obj-$(CONFIG_MSM_PIL_GSS) += pil-gss.o
obj-$(CONFIG_MSM_PIL_PRONTO) += pil-pronto.o
obj-$(CONFIG_MSM_PIL_VENUS) += pil-venus.o
obj-$(CONFIG_MSM_PIL_VPU) += pil-vpu.o
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
obj-$(CONFIG_ARCH_FSM9XXX) += sirc-fsm9xxx.o
obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o
+18 −0
Original line number Diff line number Diff line
@@ -6294,6 +6294,24 @@ static struct clk_lookup apq_clocks_8084[] = {
						"fde0b000.qcom,vpu"),
	CLK_LOOKUP("prng_clk", gcc_prng_ahb_clk.c, "fde0b000.qcom,vpu"),

	CLK_LOOKUP("iface_clk", vpu_ahb_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("bus_clk", vpu_axi_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("vdp_clk", vpu_vdp_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("vdp_bus_clk", vpu_bus_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("cxo_clk", vpu_cxo_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("core_clk", vpu_maple_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("sleep_clk", vpu_sleep_clk.c, "fde0b000.qcom,pil-vpu"),
	CLK_LOOKUP("maple_bus_clk", gcc_mmss_vpu_maple_sys_noc_axi_clk.c,
						"fde0b000.qcom,pil-vpu"),

	CLK_LOOKUP("",	vpu_ahb_clk.c,	""),
	CLK_LOOKUP("",	vpu_axi_clk.c,	""),
	CLK_LOOKUP("",	vpu_bus_clk.c,	""),
	CLK_LOOKUP("",	vpu_cxo_clk.c,	""),
	CLK_LOOKUP("",	vpu_maple_clk.c,	""),
	CLK_LOOKUP("",	vpu_sleep_clk.c,	""),
	CLK_LOOKUP("",	vpu_vdp_clk.c,	""),

	/* IOMMU clocks */
	CLK_LOOKUP("iface_clk", camss_jpeg_jpeg_ahb_clk.c,
						"fda64000.qcom,iommu"),
+338 −0
Original line number Diff line number Diff line
/* Copyright (c)2013, The Linux Foundation. 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 version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>

#include <mach/subsystem_restart.h>
#include <mach/msm_bus_board.h>
#include <mach/msm_bus.h>
#include <mach/ramdump.h>

#include "peripheral-loader.h"
#include "scm-pas.h"

/* PIL proxy vote timeout */
#define VPU_PROXY_TIMEOUT_MS				10000

static const char * const clk_names[] = {
	"core_clk",
	"iface_clk",
	"bus_clk",
	"vdp_clk",
	"vdp_bus_clk",
	"cxo_clk",
	"sleep_clk",
	"maple_bus_clk"
};

struct vpu_data {
	struct pil_desc desc;
	struct subsys_device *subsys;
	struct subsys_desc subsys_desc;
	struct regulator *gdsc;
	struct clk *clks[ARRAY_SIZE(clk_names)];
	void *ramdump_dev;
};

#define subsys_to_drv(d) container_of(d, struct vpu_data, subsys_desc)

/* Get vpu clocks and set rates for rate-settable clocks */
static int vpu_clock_setup(struct device *dev)
{
	struct vpu_data *drv = dev_get_drvdata(dev);
	int i;

	for (i = 0; i < ARRAY_SIZE(drv->clks); i++) {
		drv->clks[i] = devm_clk_get(dev, clk_names[i]);
		if (IS_ERR(drv->clks[i])) {
			dev_err(dev, "failed to get %s\n",
				clk_names[i]);
			return PTR_ERR(drv->clks[i]);
		}
		/* Make sure rate-settable clocks' rates are set */
		if (clk_get_rate(drv->clks[i]) == 0)
			clk_set_rate(drv->clks[i],
				     clk_round_rate(drv->clks[i], 0));
	}

	return 0;
}

static int vpu_clock_prepare_enable(struct device *dev)
{
	struct vpu_data *drv = dev_get_drvdata(dev);
	int rc, i;

	for (i = 0; i < ARRAY_SIZE(drv->clks); i++) {
		rc = clk_prepare_enable(drv->clks[i]);
		if (rc) {
			dev_err(dev, "failed to enable %s\n",
				clk_names[i]);
			for (i--; i >= 0; i--)
				clk_disable_unprepare(drv->clks[i]);
			return rc;
		}
	}

	return 0;
}

static void vpu_clock_disable_unprepare(struct device *dev)
{
	struct vpu_data *drv = dev_get_drvdata(dev);
	int i;

	for (i = 0; i < ARRAY_SIZE(drv->clks); i++)
		clk_disable_unprepare(drv->clks[i]);
}

static int pil_vpu_make_proxy_vote(struct pil_desc *pil)
{
	struct vpu_data *drv = dev_get_drvdata(pil->dev);
	int rc;

	/*
	 * Clocks need to be proxy voted to be able to pass control
	 * of clocks from PIL driver to the VPU driver. But GDSC
	 * needs to be turned on before clocks can be turned on. So
	 * enable the GDSC here.
	 */
	rc = regulator_enable(drv->gdsc);
	if (rc) {
		dev_err(pil->dev, "GDSC enable failed\n");
		goto err_regulator;
	}

	rc = vpu_clock_prepare_enable(pil->dev);
	if (rc) {
		dev_err(pil->dev, "clock prepare and enable failed\n");
		goto err_clock;
	}

	return 0;

err_clock:
	regulator_disable(drv->gdsc);
err_regulator:
	return rc;
}

static void pil_vpu_remove_proxy_vote(struct pil_desc *pil)
{
	struct vpu_data *drv = dev_get_drvdata(pil->dev);

	vpu_clock_disable_unprepare(pil->dev);

	/* Disable GDSC */
	regulator_disable(drv->gdsc);
}

static int pil_vpu_init_image_trusted(struct pil_desc *pil,
		const u8 *metadata, size_t size)
{
	return pas_init_image(PAS_VPU, metadata, size);
}

static int pil_vpu_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
			       size_t size)
{
	return pas_mem_setup(PAS_VPU, addr, size);
}

static int pil_vpu_reset_trusted(struct pil_desc *pil)
{
	int rc;
	struct vpu_data *drv = dev_get_drvdata(pil->dev);

	/*
	 * GDSC needs to remain on till VPU is shutdown. So, enable
	 * the GDSC here again to make sure it remains on beyond the
	 * expiry of the proxy vote timer.
	 */
	rc = regulator_enable(drv->gdsc);
	if (rc) {
		dev_err(pil->dev, "GDSC enable failed\n");
		return rc;
	}

	rc = pas_auth_and_reset(PAS_VPU);
	if (rc)
		regulator_disable(drv->gdsc);


	return rc;
}

static int pil_vpu_shutdown_trusted(struct pil_desc *pil)
{
	int rc;
	struct vpu_data *drv = dev_get_drvdata(pil->dev);

	vpu_clock_prepare_enable(pil->dev);

	rc = pas_shutdown(PAS_VPU);

	vpu_clock_disable_unprepare(pil->dev);

	regulator_disable(drv->gdsc);

	return rc;
}

static struct pil_reset_ops pil_vpu_ops_trusted = {
	.init_image = pil_vpu_init_image_trusted,
	.mem_setup = pil_vpu_mem_setup_trusted,
	.auth_and_reset = pil_vpu_reset_trusted,
	.shutdown = pil_vpu_shutdown_trusted,
	.proxy_vote = pil_vpu_make_proxy_vote,
	.proxy_unvote = pil_vpu_remove_proxy_vote,
};

static int vpu_shutdown(const struct subsys_desc *desc, bool force_stop)
{
	struct vpu_data *drv = subsys_to_drv(desc);

	pil_shutdown(&drv->desc);

	return 0;
}

static int vpu_powerup(const struct subsys_desc *desc)
{
	struct vpu_data *drv = subsys_to_drv(desc);

	return pil_boot(&drv->desc);
}

static int vpu_ramdump(int enable, const struct subsys_desc *desc)
{
	struct vpu_data *drv = subsys_to_drv(desc);

	if (!enable)
		return 0;

	return pil_do_ramdump(&drv->desc, drv->ramdump_dev);
}

static int pil_vpu_probe(struct platform_device *pdev)
{
	struct vpu_data *drv;
	struct pil_desc *desc;
	int rc;

	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
	if (!drv)
		return -ENOMEM;
	platform_set_drvdata(pdev, drv);

	drv->gdsc = devm_regulator_get(&pdev->dev, "vdd");
	if (IS_ERR(drv->gdsc)) {
		dev_err(&pdev->dev, "Failed to get VPU GDSC\n");
		return -ENODEV;
	}

	rc = vpu_clock_setup(&pdev->dev);
	if (rc) {
		dev_err(&pdev->dev, "Failed to setup VPU clocks\n");
		return rc;
	}

	desc = &drv->desc;
	rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
					&desc->name);
	if (rc) {
		dev_err(&pdev->dev, "Failed to read the firmware name\n");
		return rc;
	}

	desc->dev = &pdev->dev;
	desc->owner = THIS_MODULE;
	desc->proxy_timeout = VPU_PROXY_TIMEOUT_MS;

	rc = pas_supported(PAS_VPU);
	if (rc > 0) {
		desc->ops = &pil_vpu_ops_trusted;
		dev_info(&pdev->dev, "using secure boot\n");
	} else {
		dev_err(&pdev->dev, "Secure boot is not supported\n");
		return rc;
	}

	drv->ramdump_dev = create_ramdump_device("vpu", &pdev->dev);
	if (!drv->ramdump_dev)
		return -ENOMEM;

	rc = pil_desc_init(desc);
	if (rc)
		goto err_ramdump;

	scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);

	drv->subsys_desc.name = desc->name;
	drv->subsys_desc.owner = THIS_MODULE;
	drv->subsys_desc.dev = &pdev->dev;
	drv->subsys_desc.shutdown = vpu_shutdown;
	drv->subsys_desc.powerup = vpu_powerup;
	drv->subsys_desc.ramdump = vpu_ramdump;

	drv->subsys = subsys_register(&drv->subsys_desc);
	if (IS_ERR(drv->subsys)) {
		rc = PTR_ERR(drv->subsys);
		goto err_subsys;
	}
	return rc;

err_subsys:
	pil_desc_release(desc);
err_ramdump:
	destroy_ramdump_device(drv->ramdump_dev);

	return rc;
}

static int pil_vpu_remove(struct platform_device *pdev)
{
	struct vpu_data *drv = platform_get_drvdata(pdev);
	subsys_unregister(drv->subsys);
	pil_desc_release(&drv->desc);

	return 0;
}

static const struct of_device_id msm_pil_vpu_match[] = {
	{.compatible = "qcom,pil-vpu"},
	{}
};

static struct platform_driver pil_vpu_driver = {
	.probe = pil_vpu_probe,
	.remove = pil_vpu_remove,
	.driver = {
		.name = "pil_vpu",
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(msm_pil_vpu_match),
	},
};

module_platform_driver(pil_vpu_driver);

MODULE_DESCRIPTION("Support for booting Maple processors");
MODULE_LICENSE("GPL v2");
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ enum pas_id {
	PAS_SECAPP,
	PAS_GSS,
	PAS_VIDC,
	PAS_VPU,
};

#ifdef CONFIG_MSM_PIL