summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/qualcomm/ppe/ppe.c
blob: be747510d94775441fd3b3eb5e4b8a4cdbbc0470 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

/* PPE platform device probe, DTSI parser and PPE clock initializations. */

#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>

#include "ppe.h"
#include "ppe_config.h"
#include "ppe_debugfs.h"

#define PPE_PORT_MAX		8
#define PPE_CLK_RATE		353000000

/* ICC clocks for enabling PPE device. The avg_bw and peak_bw with value 0
 * will be updated by the clock rate of PPE.
 */
static const struct icc_bulk_data ppe_icc_data[] = {
	{
		.name = "ppe",
		.avg_bw = 0,
		.peak_bw = 0,
	},
	{
		.name = "ppe_cfg",
		.avg_bw = 0,
		.peak_bw = 0,
	},
	{
		.name = "qos_gen",
		.avg_bw = 6000,
		.peak_bw = 6000,
	},
	{
		.name = "timeout_ref",
		.avg_bw = 6000,
		.peak_bw = 6000,
	},
	{
		.name = "nssnoc_memnoc",
		.avg_bw = 533333,
		.peak_bw = 533333,
	},
	{
		.name = "memnoc_nssnoc",
		.avg_bw = 533333,
		.peak_bw = 533333,
	},
	{
		.name = "memnoc_nssnoc_1",
		.avg_bw = 533333,
		.peak_bw = 533333,
	},
};

static const struct regmap_range ppe_readable_ranges[] = {
	regmap_reg_range(0x0, 0x1ff),		/* Global */
	regmap_reg_range(0x400, 0x5ff),		/* LPI CSR */
	regmap_reg_range(0x1000, 0x11ff),	/* GMAC0 */
	regmap_reg_range(0x1200, 0x13ff),	/* GMAC1 */
	regmap_reg_range(0x1400, 0x15ff),	/* GMAC2 */
	regmap_reg_range(0x1600, 0x17ff),	/* GMAC3 */
	regmap_reg_range(0x1800, 0x19ff),	/* GMAC4 */
	regmap_reg_range(0x1a00, 0x1bff),	/* GMAC5 */
	regmap_reg_range(0xb000, 0xefff),	/* PRX CSR */
	regmap_reg_range(0xf000, 0x1efff),	/* IPE */
	regmap_reg_range(0x20000, 0x5ffff),	/* PTX CSR */
	regmap_reg_range(0x60000, 0x9ffff),	/* IPE L2 CSR */
	regmap_reg_range(0xb0000, 0xeffff),	/* IPO CSR */
	regmap_reg_range(0x100000, 0x17ffff),	/* IPE PC */
	regmap_reg_range(0x180000, 0x1bffff),	/* PRE IPO CSR */
	regmap_reg_range(0x1d0000, 0x1dffff),	/* Tunnel parser */
	regmap_reg_range(0x1e0000, 0x1effff),	/* Ingress parse */
	regmap_reg_range(0x200000, 0x2fffff),	/* IPE L3 */
	regmap_reg_range(0x300000, 0x3fffff),	/* IPE tunnel */
	regmap_reg_range(0x400000, 0x4fffff),	/* Scheduler */
	regmap_reg_range(0x500000, 0x503fff),	/* XGMAC0 */
	regmap_reg_range(0x504000, 0x507fff),	/* XGMAC1 */
	regmap_reg_range(0x508000, 0x50bfff),	/* XGMAC2 */
	regmap_reg_range(0x50c000, 0x50ffff),	/* XGMAC3 */
	regmap_reg_range(0x510000, 0x513fff),	/* XGMAC4 */
	regmap_reg_range(0x514000, 0x517fff),	/* XGMAC5 */
	regmap_reg_range(0x600000, 0x6fffff),	/* BM */
	regmap_reg_range(0x800000, 0x9fffff),	/* QM */
	regmap_reg_range(0xb00000, 0xbef800),	/* EDMA */
};

static const struct regmap_access_table ppe_reg_table = {
	.yes_ranges = ppe_readable_ranges,
	.n_yes_ranges = ARRAY_SIZE(ppe_readable_ranges),
};

static const struct regmap_config regmap_config_ipq9574 = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.rd_table = &ppe_reg_table,
	.wr_table = &ppe_reg_table,
	.max_register = 0xbef800,
	.fast_io = true,
};

static int ppe_clock_init_and_reset(struct ppe_device *ppe_dev)
{
	unsigned long ppe_rate = ppe_dev->clk_rate;
	struct device *dev = ppe_dev->dev;
	struct reset_control *rstc;
	struct clk_bulk_data *clks;
	struct clk *clk;
	int ret, i;

	for (i = 0; i < ppe_dev->num_icc_paths; i++) {
		ppe_dev->icc_paths[i].name = ppe_icc_data[i].name;
		ppe_dev->icc_paths[i].avg_bw = ppe_icc_data[i].avg_bw ? :
					       Bps_to_icc(ppe_rate);

		/* PPE does not have an explicit peak bandwidth requirement,
		 * so set the peak bandwidth to be equal to the average
		 * bandwidth.
		 */
		ppe_dev->icc_paths[i].peak_bw = ppe_icc_data[i].peak_bw ? :
						Bps_to_icc(ppe_rate);
	}

	ret = devm_of_icc_bulk_get(dev, ppe_dev->num_icc_paths,
				   ppe_dev->icc_paths);
	if (ret)
		return ret;

	ret = icc_bulk_set_bw(ppe_dev->num_icc_paths, ppe_dev->icc_paths);
	if (ret)
		return ret;

	/* The PPE clocks have a common parent clock. Setting the clock
	 * rate of "ppe" ensures the clock rate of all PPE clocks is
	 * configured to the same rate.
	 */
	clk = devm_clk_get(dev, "ppe");
	if (IS_ERR(clk))
		return PTR_ERR(clk);

	ret = clk_set_rate(clk, ppe_rate);
	if (ret)
		return ret;

	ret = devm_clk_bulk_get_all_enabled(dev, &clks);
	if (ret < 0)
		return ret;

	/* Reset the PPE. */
	rstc = devm_reset_control_get_exclusive(dev, NULL);
	if (IS_ERR(rstc))
		return PTR_ERR(rstc);

	ret = reset_control_assert(rstc);
	if (ret)
		return ret;

	/* The delay 10 ms of assert is necessary for resetting PPE. */
	usleep_range(10000, 11000);

	return reset_control_deassert(rstc);
}

static int qcom_ppe_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct ppe_device *ppe_dev;
	void __iomem *base;
	int ret, num_icc;

	num_icc = ARRAY_SIZE(ppe_icc_data);
	ppe_dev = devm_kzalloc(dev, struct_size(ppe_dev, icc_paths, num_icc),
			       GFP_KERNEL);
	if (!ppe_dev)
		return -ENOMEM;

	base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(base))
		return dev_err_probe(dev, PTR_ERR(base), "PPE ioremap failed\n");

	ppe_dev->regmap = devm_regmap_init_mmio(dev, base, &regmap_config_ipq9574);
	if (IS_ERR(ppe_dev->regmap))
		return dev_err_probe(dev, PTR_ERR(ppe_dev->regmap),
				     "PPE initialize regmap failed\n");
	ppe_dev->dev = dev;
	ppe_dev->clk_rate = PPE_CLK_RATE;
	ppe_dev->num_ports = PPE_PORT_MAX;
	ppe_dev->num_icc_paths = num_icc;

	ret = ppe_clock_init_and_reset(ppe_dev);
	if (ret)
		return dev_err_probe(dev, ret, "PPE clock config failed\n");

	ret = ppe_hw_config(ppe_dev);
	if (ret)
		return dev_err_probe(dev, ret, "PPE HW config failed\n");

	ppe_debugfs_setup(ppe_dev);
	platform_set_drvdata(pdev, ppe_dev);

	return 0;
}

static void qcom_ppe_remove(struct platform_device *pdev)
{
	struct ppe_device *ppe_dev;

	ppe_dev = platform_get_drvdata(pdev);
	ppe_debugfs_teardown(ppe_dev);
}

static const struct of_device_id qcom_ppe_of_match[] = {
	{ .compatible = "qcom,ipq9574-ppe" },
	{}
};
MODULE_DEVICE_TABLE(of, qcom_ppe_of_match);

static struct platform_driver qcom_ppe_driver = {
	.driver = {
		.name = "qcom_ppe",
		.of_match_table = qcom_ppe_of_match,
	},
	.probe	= qcom_ppe_probe,
	.remove = qcom_ppe_remove,
};
module_platform_driver(qcom_ppe_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ PPE driver");