summaryrefslogtreecommitdiff
path: root/drivers/bus/stm32_etzpc.c
blob: 7fc0f16960be3edbbd2752d4bec13c66e6696fec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
 */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/types.h>

#include "stm32_firewall.h"

/*
 * ETZPC registers
 */
#define ETZPC_DECPROT			0x10
#define ETZPC_HWCFGR			0x3F0

/*
 * HWCFGR register
 */
#define ETZPC_HWCFGR_NUM_TZMA		GENMASK(7, 0)
#define ETZPC_HWCFGR_NUM_PER_SEC	GENMASK(15, 8)
#define ETZPC_HWCFGR_NUM_AHB_SEC	GENMASK(23, 16)
#define ETZPC_HWCFGR_CHUNKS1N4		GENMASK(31, 24)

/*
 * ETZPC miscellaneous
 */
#define ETZPC_PROT_MASK			GENMASK(1, 0)
#define ETZPC_PROT_A7NS			0x3
#define ETZPC_DECPROT_SHIFT		1

#define IDS_PER_DECPROT_REGS		16

static int stm32_etzpc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
	u32 offset, reg_offset, sec_val;

	if (firewall_id >= ctrl->max_entries) {
		dev_err(ctrl->dev, "Invalid sys bus ID %u", firewall_id);
		return -EINVAL;
	}

	/* Check access configuration, 16 peripherals per register */
	reg_offset = ETZPC_DECPROT + 0x4 * (firewall_id / IDS_PER_DECPROT_REGS);
	offset = (firewall_id % IDS_PER_DECPROT_REGS) << ETZPC_DECPROT_SHIFT;

	/* Verify peripheral is non-secure and attributed to cortex A7 */
	sec_val = (readl(ctrl->mmio + reg_offset) >> offset) & ETZPC_PROT_MASK;
	if (sec_val != ETZPC_PROT_A7NS) {
		dev_dbg(ctrl->dev, "Invalid bus configuration: reg_offset %#x, value %d\n",
			reg_offset, sec_val);
		return -EACCES;
	}

	return 0;
}

static void stm32_etzpc_release_access(struct stm32_firewall_controller *ctrl __maybe_unused,
				       u32 firewall_id __maybe_unused)
{
}

static int stm32_etzpc_probe(struct platform_device *pdev)
{
	struct stm32_firewall_controller *etzpc_controller;
	struct device_node *np = pdev->dev.of_node;
	u32 nb_per, nb_master;
	struct resource *res;
	void __iomem *mmio;
	int rc;

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

	mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
	if (IS_ERR(mmio))
		return PTR_ERR(mmio);

	etzpc_controller->dev = &pdev->dev;
	etzpc_controller->mmio = mmio;
	etzpc_controller->name = dev_driver_string(etzpc_controller->dev);
	etzpc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
	etzpc_controller->grant_access = stm32_etzpc_grant_access;
	etzpc_controller->release_access = stm32_etzpc_release_access;

	/* Get number of etzpc entries*/
	nb_per = FIELD_GET(ETZPC_HWCFGR_NUM_PER_SEC,
			   readl(etzpc_controller->mmio + ETZPC_HWCFGR));
	nb_master = FIELD_GET(ETZPC_HWCFGR_NUM_AHB_SEC,
			      readl(etzpc_controller->mmio + ETZPC_HWCFGR));
	etzpc_controller->max_entries = nb_per + nb_master;

	platform_set_drvdata(pdev, etzpc_controller);

	rc = stm32_firewall_controller_register(etzpc_controller);
	if (rc) {
		dev_err(etzpc_controller->dev, "Couldn't register as a firewall controller: %d",
			rc);
		return rc;
	}

	rc = stm32_firewall_populate_bus(etzpc_controller);
	if (rc) {
		dev_err(etzpc_controller->dev, "Couldn't populate ETZPC bus: %d",
			rc);
		return rc;
	}

	/* Populate all allowed nodes */
	return of_platform_populate(np, NULL, NULL, &pdev->dev);
}

static const struct of_device_id stm32_etzpc_of_match[] = {
	{ .compatible = "st,stm32-etzpc" },
	{}
};
MODULE_DEVICE_TABLE(of, stm32_etzpc_of_match);

static struct platform_driver stm32_etzpc_driver = {
	.probe  = stm32_etzpc_probe,
	.driver = {
		.name = "stm32-etzpc",
		.of_match_table = stm32_etzpc_of_match,
	},
};
module_platform_driver(stm32_etzpc_driver);

MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
MODULE_DESCRIPTION("STMicroelectronics ETZPC driver");
MODULE_LICENSE("GPL");