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

Commit 30adbe2a authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: add secure processor subsystem (spss) utils driver"

parents 37e2859d e288cd0a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -519,6 +519,17 @@ config QSEE_IPC_IRQ
	  Clients can use this driver to avoid adding common interrupt handling
	  code.

config MSM_SPSS_UTILS
	depends on MSM_PIL
	bool "Secure Processor Utilities"
	help
	  spss-utils driver selects Secure Processor firmware file name.
	  The firmware file name for dev, test or production is selected
	  based on two fuses.
	  Different file name is used for differnt SPSS HW versions,
	  because the SPSS firmware size is too small to support multiple
	  HW versions.

config QSEE_IPC_IRQ_BRIDGE
	tristate "QSEE IPC Interrupt Bridge"
	select QSEE_IPC_IRQ
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ obj-$(CONFIG_MSM_TZ_SMMU) += msm_tz_smmu.o
CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
obj-$(CONFIG_QCOM_SCM)  +=      scm.o
obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o
obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o
obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
+393 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 */

/*
 * Secure-Processor-SubSystem (SPSS) utilities.
 *
 * This driver provides utilities for the Secure Processor (SP).
 *
 * The SP daemon needs to load different SPSS images based on:
 *
 * 1. Test/Production key used to sign the SPSS image (read fuses).
 * 2. SPSS HW version (selected via Device Tree).
 *
 */

#define pr_fmt(fmt)	"spss_utils [%s]: " fmt, __func__

#include <linux/kernel.h>   /* min() */
#include <linux/module.h>   /* MODULE_LICENSE */
#include <linux/device.h>   /* class_create() */
#include <linux/slab.h>     /* kzalloc() */
#include <linux/fs.h>       /* file_operations */
#include <linux/cdev.h>     /* cdev_add() */
#include <linux/errno.h>    /* EINVAL, ETIMEDOUT */
#include <linux/printk.h>   /* pr_err() */
#include <linux/bitops.h>   /* BIT(x) */
#include <linux/platform_device.h> /* platform_driver_register() */
#include <linux/of.h>       /* of_property_count_strings() */
#include <linux/io.h>       /* ioremap_nocache() */

#include <soc/qcom/subsystem_restart.h>

/* driver name */
#define DEVICE_NAME	"spss-utils"

enum spss_firmware_type {
	SPSS_FW_TYPE_DEV = 'd',
	SPSS_FW_TYPE_TEST = 't',
	SPSS_FW_TYPE_PROD = 'p',
	SPSS_FW_TYPE_NONE = 'z',
};

static enum spss_firmware_type firmware_type = SPSS_FW_TYPE_TEST;
static const char *dev_firmware_name;
static const char *test_firmware_name;
static const char *prod_firmware_name;
static const char *none_firmware_name = "nospss";
static const char *firmware_name = "NA";
static struct device *spss_dev;
static u32 spss_debug_reg_addr; /* SP_SCSR_MBn_SP2CL_GPm(n,m) */
static u32 spss_emul_type_reg_addr; /* TCSR_SOC_EMULATION_TYPE */

#define SPU_EMULATUION (BIT(0) | BIT(1))
#define SPU_PRESENT_IN_EMULATION BIT(2)

/*==========================================================================*/
/*		Device Sysfs */
/*==========================================================================*/

static ssize_t firmware_name_show(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	int ret;

	if (firmware_name == NULL)
		ret = scnprintf(buf, PAGE_SIZE, "%s\n", "unknown");
	else
		ret = scnprintf(buf, PAGE_SIZE, "%s\n", firmware_name);

	return ret;
}

static DEVICE_ATTR_RO(firmware_name);

static ssize_t test_fuse_state_show(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	int ret;

	switch (firmware_type) {
	case SPSS_FW_TYPE_DEV:
		ret = scnprintf(buf, PAGE_SIZE, "%s", "dev");
		break;
	case SPSS_FW_TYPE_TEST:
		ret = scnprintf(buf, PAGE_SIZE, "%s", "test");
		break;
	case SPSS_FW_TYPE_PROD:
		ret = scnprintf(buf, PAGE_SIZE, "%s", "prod");
		break;
	default:
		return -EINVAL;
	}

	return ret;
}

static DEVICE_ATTR_RO(test_fuse_state);

static ssize_t spss_debug_reg_show(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	int ret;
	void __iomem *spss_debug_reg = NULL;
	u32 val1, val2;

	pr_debug("spss_debug_reg_addr [0x%x]\n", spss_debug_reg_addr);

	spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, sizeof(u32)*2);

	if (!spss_debug_reg) {
		pr_err("can't map debug reg addr\n");
		return -EINVAL;
	}

	val1 = readl_relaxed(spss_debug_reg);
	val2 = readl_relaxed(((char *) spss_debug_reg) + sizeof(u32));

	ret = scnprintf(buf, PAGE_SIZE, "val1 [0x%x] val2 [0x%x]\n",
			val1, val2);

	iounmap(spss_debug_reg);

	return ret;
}

static DEVICE_ATTR_RO(spss_debug_reg);

static int spss_create_sysfs(struct device *dev)
{
	int ret;

	ret = device_create_file(dev, &dev_attr_firmware_name);
	if (ret < 0) {
		pr_err("failed to create sysfs file for firmware_name\n");
		return ret;
	}

	ret = device_create_file(dev, &dev_attr_test_fuse_state);
	if (ret < 0) {
		pr_err("failed to create sysfs file for test_fuse_state\n");
		device_remove_file(dev, &dev_attr_firmware_name);
		return ret;
	}

	ret = device_create_file(dev, &dev_attr_spss_debug_reg);
	if (ret < 0) {
		pr_err("failed to create sysfs file for spss_debug_reg\n");
		device_remove_file(dev, &dev_attr_firmware_name);
		device_remove_file(dev, &dev_attr_test_fuse_state);
		return ret;
	}

	return 0;
}

/*==========================================================================*/
/*		Device Tree */
/*==========================================================================*/

/**
 * spss_parse_dt() - Parse Device Tree info.
 */
static int spss_parse_dt(struct device_node *node)
{
	int ret;
	u32 spss_fuse1_addr = 0;
	u32 spss_fuse1_bit = 0;
	u32 spss_fuse1_mask = 0;
	void __iomem *spss_fuse1_reg = NULL;
	u32 spss_fuse2_addr = 0;
	u32 spss_fuse2_bit = 0;
	u32 spss_fuse2_mask = 0;
	void __iomem *spss_fuse2_reg = NULL;
	u32 val1 = 0;
	u32 val2 = 0;
	void __iomem *spss_emul_type_reg = NULL;
	u32 spss_emul_type_val = 0;

	ret = of_property_read_string(node, "qcom,spss-dev-firmware-name",
		&dev_firmware_name);
	if (ret < 0) {
		pr_err("can't get dev fw name\n");
		return -EINVAL;
	}

	ret = of_property_read_string(node, "qcom,spss-test-firmware-name",
		&test_firmware_name);
	if (ret < 0) {
		pr_err("can't get test fw name\n");
		return -EINVAL;
	}

	ret = of_property_read_string(node, "qcom,spss-prod-firmware-name",
		&prod_firmware_name);
	if (ret < 0) {
		pr_err("can't get prod fw name\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(node, "qcom,spss-fuse1-addr",
		&spss_fuse1_addr);
	if (ret < 0) {
		pr_err("can't get fuse1 addr\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(node, "qcom,spss-fuse2-addr",
		&spss_fuse2_addr);
	if (ret < 0) {
		pr_err("can't get fuse2 addr\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(node, "qcom,spss-fuse1-bit",
		&spss_fuse1_bit);
	if (ret < 0) {
		pr_err("can't get fuse1 bit\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(node, "qcom,spss-fuse2-bit",
		&spss_fuse2_bit);
	if (ret < 0) {
		pr_err("can't get fuse2 bit\n");
		return -EINVAL;
	}


	spss_fuse1_mask = BIT(spss_fuse1_bit);
	spss_fuse2_mask = BIT(spss_fuse2_bit);

	pr_debug("spss fuse1 addr [0x%x] bit [%d]\n",
		(int) spss_fuse1_addr, (int) spss_fuse1_bit);
	pr_debug("spss fuse2 addr [0x%x] bit [%d]\n",
		(int) spss_fuse2_addr, (int) spss_fuse2_bit);

	spss_fuse1_reg = ioremap_nocache(spss_fuse1_addr, sizeof(u32));

	if (!spss_fuse1_reg) {
		pr_err("can't map fuse1 addr\n");
		return -EINVAL;
	}

	spss_fuse2_reg = ioremap_nocache(spss_fuse2_addr, sizeof(u32));

	if (!spss_fuse2_reg) {
		iounmap(spss_fuse1_reg);
		pr_err("can't map fuse2 addr\n");
		return -EINVAL;
	}

	val1 = readl_relaxed(spss_fuse1_reg);
	val2 = readl_relaxed(spss_fuse2_reg);

	pr_debug("spss fuse1 value [0x%08x]\n", (int) val1);
	pr_debug("spss fuse2 value [0x%08x]\n", (int) val2);

	pr_debug("spss fuse1 mask [0x%08x]\n", (int) spss_fuse1_mask);
	pr_debug("spss fuse2 mask [0x%08x]\n", (int) spss_fuse2_mask);

	/**
	 * Set firmware_type based on fuses:
	 *	SPSS_CONFIG_MODE 11:        dev
	 *	SPSS_CONFIG_MODE 01 or 10:  test
	 *	SPSS_CONFIG_MODE 00:        prod
	 */
	if ((val1 & spss_fuse1_mask) && (val2 & spss_fuse2_mask))
		firmware_type = SPSS_FW_TYPE_DEV;
	else if ((val1 & spss_fuse1_mask) || (val2 & spss_fuse2_mask))
		firmware_type = SPSS_FW_TYPE_TEST;
	else
		firmware_type = SPSS_FW_TYPE_PROD;

	iounmap(spss_fuse1_reg);
	iounmap(spss_fuse2_reg);

	ret = of_property_read_u32(node, "qcom,spss-debug-reg-addr",
		&spss_debug_reg_addr);
	if (ret < 0) {
		pr_err("can't get debug regs addr\n");
		return ret;
	}

	ret = of_property_read_u32(node, "qcom,spss-emul-type-reg-addr",
			     &spss_emul_type_reg_addr);
	if (ret < 0) {
		pr_err("can't get spss-emulation-type-reg addr\n");
		return -EINVAL;
	}

	spss_emul_type_reg = ioremap_nocache(spss_emul_type_reg_addr,
					     sizeof(u32));
	if (!spss_emul_type_reg) {
		pr_err("can't map soc-emulation-type reg addr\n");
		return -EINVAL;
	}

	spss_emul_type_val = readl_relaxed(spss_emul_type_reg);

	pr_debug("spss_emul_type value [0x%08x]\n", (int)spss_emul_type_val);
	if ((spss_emul_type_val & SPU_EMULATUION) &&
	    !(spss_emul_type_val & SPU_PRESENT_IN_EMULATION)) {
		/* for some emulation platforms SPSS is not present */
		firmware_type = SPSS_FW_TYPE_NONE;
	}
	iounmap(spss_emul_type_reg);

	return 0;
}

/**
 * spss_probe() - initialization sequence
 */
static int spss_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct device_node *np = NULL;
	struct device *dev = NULL;

	np = pdev->dev.of_node;
	dev = &pdev->dev;
	spss_dev = dev;
	platform_set_drvdata(pdev, dev);

	ret = spss_parse_dt(np);
	if (ret < 0)
		return ret;

	switch (firmware_type) {
	case SPSS_FW_TYPE_DEV:
		firmware_name = dev_firmware_name;
		break;
	case SPSS_FW_TYPE_TEST:
		firmware_name = test_firmware_name;
		break;
	case SPSS_FW_TYPE_PROD:
		firmware_name = prod_firmware_name;
		break;
	case SPSS_FW_TYPE_NONE:
		firmware_name = none_firmware_name;
		break;
	default:
		return -EINVAL;
	}

	ret = subsystem_set_fwname("spss", firmware_name);
	if (ret < 0) {
		pr_err("fail to set fw name\n");
		return -EINVAL;
	}

	ret = spss_create_sysfs(dev);
	if (ret < 0)
		return ret;

	return 0;
}

static const struct of_device_id spss_match_table[] = {
	{ .compatible = "qcom,spss-utils", },
	{ },
};

static struct platform_driver spss_driver = {
	.probe = spss_probe,
	.driver = {
		.name = DEVICE_NAME,
		.of_match_table = of_match_ptr(spss_match_table),
	},
};

/*==========================================================================*/
/*		Driver Init/Exit					*/
/*==========================================================================*/
static int __init spss_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&spss_driver);
	if (ret)
		pr_err("register platform driver failed, ret [%d]\n", ret);

	return ret;
}
late_initcall(spss_init); /* start after PIL driver */

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Secure Processor Utilities");