Commit 220723dc authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-ipa-interconnect-improvements'

Alex Elder says:

====================
net: ipa: interconnect improvements

The main outcome of this series is to allow the number of
interconnects used by the IPA to differ from the three that
are implemented now.  With this series in place, any number
of interconnects can now be used, all specified in the
configuration data for a specific platform.

A few minor interconnect-related cleanups are implemented as well.
====================

Link: https://lore.kernel.org/r/20210115125050.20555-1-elder@linaro.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 213b97b1 ea151e19
...@@ -30,143 +30,155 @@ ...@@ -30,143 +30,155 @@
* An IPA clock reference must be held for any access to IPA hardware. * An IPA clock reference must be held for any access to IPA hardware.
*/ */
/**
* struct ipa_interconnect - IPA interconnect information
* @path: Interconnect path
* @average_bandwidth: Average interconnect bandwidth (KB/second)
* @peak_bandwidth: Peak interconnect bandwidth (KB/second)
*/
struct ipa_interconnect {
struct icc_path *path;
u32 average_bandwidth;
u32 peak_bandwidth;
};
/** /**
* struct ipa_clock - IPA clocking information * struct ipa_clock - IPA clocking information
* @count: Clocking reference count * @count: Clocking reference count
* @mutex: Protects clock enable/disable * @mutex: Protects clock enable/disable
* @core: IPA core clock * @core: IPA core clock
* @memory_path: Memory interconnect * @interconnect_count: Number of elements in interconnect[]
* @imem_path: Internal memory interconnect * @interconnect: Interconnect array
* @config_path: Configuration space interconnect
* @interconnect_data: Interconnect configuration data
*/ */
struct ipa_clock { struct ipa_clock {
refcount_t count; refcount_t count;
struct mutex mutex; /* protects clock enable/disable */ struct mutex mutex; /* protects clock enable/disable */
struct clk *core; struct clk *core;
struct icc_path *memory_path; u32 interconnect_count;
struct icc_path *imem_path; struct ipa_interconnect *interconnect;
struct icc_path *config_path;
const struct ipa_interconnect_data *interconnect_data;
}; };
static struct icc_path * static int ipa_interconnect_init_one(struct device *dev,
ipa_interconnect_init_one(struct device *dev, const char *name) struct ipa_interconnect *interconnect,
const struct ipa_interconnect_data *data)
{ {
struct icc_path *path; struct icc_path *path;
path = of_icc_get(dev, name); path = of_icc_get(dev, data->name);
if (IS_ERR(path)) if (IS_ERR(path)) {
dev_err(dev, "error %ld getting %s interconnect\n", int ret = PTR_ERR(path);
PTR_ERR(path), name);
dev_err(dev, "error %d getting %s interconnect\n", ret,
data->name);
return path; return ret;
}
interconnect->path = path;
interconnect->average_bandwidth = data->average_bandwidth;
interconnect->peak_bandwidth = data->peak_bandwidth;
return 0;
} }
/* Initialize interconnects required for IPA operation */ static void ipa_interconnect_exit_one(struct ipa_interconnect *interconnect)
static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev)
{ {
struct icc_path *path; icc_put(interconnect->path);
memset(interconnect, 0, sizeof(*interconnect));
path = ipa_interconnect_init_one(dev, "memory"); }
if (IS_ERR(path))
goto err_return;
clock->memory_path = path;
path = ipa_interconnect_init_one(dev, "imem"); /* Initialize interconnects required for IPA operation */
if (IS_ERR(path)) static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev,
goto err_memory_path_put; const struct ipa_interconnect_data *data)
clock->imem_path = path; {
struct ipa_interconnect *interconnect;
u32 count;
int ret;
path = ipa_interconnect_init_one(dev, "config"); count = clock->interconnect_count;
if (IS_ERR(path)) interconnect = kcalloc(count, sizeof(*interconnect), GFP_KERNEL);
goto err_imem_path_put; if (!interconnect)
clock->config_path = path; return -ENOMEM;
clock->interconnect = interconnect;
while (count--) {
ret = ipa_interconnect_init_one(dev, interconnect, data++);
if (ret)
goto out_unwind;
interconnect++;
}
return 0; return 0;
err_imem_path_put: out_unwind:
icc_put(clock->imem_path); while (interconnect-- > clock->interconnect)
err_memory_path_put: ipa_interconnect_exit_one(interconnect);
icc_put(clock->memory_path); kfree(clock->interconnect);
err_return: clock->interconnect = NULL;
return PTR_ERR(path);
return ret;
} }
/* Inverse of ipa_interconnect_init() */ /* Inverse of ipa_interconnect_init() */
static void ipa_interconnect_exit(struct ipa_clock *clock) static void ipa_interconnect_exit(struct ipa_clock *clock)
{ {
icc_put(clock->config_path); struct ipa_interconnect *interconnect;
icc_put(clock->imem_path);
icc_put(clock->memory_path); interconnect = clock->interconnect + clock->interconnect_count;
while (interconnect-- > clock->interconnect)
ipa_interconnect_exit_one(interconnect);
kfree(clock->interconnect);
clock->interconnect = NULL;
} }
/* Currently we only use one bandwidth level, so just "enable" interconnects */ /* Currently we only use one bandwidth level, so just "enable" interconnects */
static int ipa_interconnect_enable(struct ipa *ipa) static int ipa_interconnect_enable(struct ipa *ipa)
{ {
const struct ipa_interconnect_data *data; struct ipa_interconnect *interconnect;
struct ipa_clock *clock = ipa->clock; struct ipa_clock *clock = ipa->clock;
int ret; int ret;
u32 i;
data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY];
ret = icc_set_bw(clock->memory_path, data->average_rate, interconnect = clock->interconnect;
data->peak_rate); for (i = 0; i < clock->interconnect_count; i++) {
if (ret) ret = icc_set_bw(interconnect->path,
return ret; interconnect->average_bandwidth,
interconnect->peak_bandwidth);
data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM]; if (ret)
ret = icc_set_bw(clock->imem_path, data->average_rate, goto out_unwind;
data->peak_rate); interconnect++;
if (ret) }
goto err_memory_path_disable;
data = &clock->interconnect_data[IPA_INTERCONNECT_CONFIG];
ret = icc_set_bw(clock->config_path, data->average_rate,
data->peak_rate);
if (ret)
goto err_imem_path_disable;
return 0; return 0;
err_imem_path_disable: out_unwind:
(void)icc_set_bw(clock->imem_path, 0, 0); while (interconnect-- > clock->interconnect)
err_memory_path_disable: (void)icc_set_bw(interconnect->path, 0, 0);
(void)icc_set_bw(clock->memory_path, 0, 0);
return ret; return ret;
} }
/* To disable an interconnect, we just its bandwidth to 0 */ /* To disable an interconnect, we just its bandwidth to 0 */
static int ipa_interconnect_disable(struct ipa *ipa) static void ipa_interconnect_disable(struct ipa *ipa)
{ {
const struct ipa_interconnect_data *data; struct ipa_interconnect *interconnect;
struct ipa_clock *clock = ipa->clock; struct ipa_clock *clock = ipa->clock;
int result = 0;
u32 count;
int ret; int ret;
ret = icc_set_bw(clock->memory_path, 0, 0); count = clock->interconnect_count;
if (ret) interconnect = clock->interconnect + count;
return ret; while (count--) {
interconnect--;
ret = icc_set_bw(clock->imem_path, 0, 0); ret = icc_set_bw(interconnect->path, 0, 0);
if (ret) if (ret && !result)
goto err_memory_path_reenable; result = ret;
}
ret = icc_set_bw(clock->config_path, 0, 0);
if (ret)
goto err_imem_path_reenable;
return 0;
err_imem_path_reenable:
data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM];
(void)icc_set_bw(clock->imem_path, data->average_rate,
data->peak_rate);
err_memory_path_reenable:
data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY];
(void)icc_set_bw(clock->memory_path, data->average_rate,
data->peak_rate);
return ret; if (result)
dev_err(&ipa->pdev->dev,
"error %d disabling IPA interconnects\n", ret);
} }
/* Turn on IPA clocks, including interconnects */ /* Turn on IPA clocks, including interconnects */
...@@ -189,7 +201,7 @@ static int ipa_clock_enable(struct ipa *ipa) ...@@ -189,7 +201,7 @@ static int ipa_clock_enable(struct ipa *ipa)
static void ipa_clock_disable(struct ipa *ipa) static void ipa_clock_disable(struct ipa *ipa)
{ {
clk_disable_unprepare(ipa->clock->core); clk_disable_unprepare(ipa->clock->core);
(void)ipa_interconnect_disable(ipa); ipa_interconnect_disable(ipa);
} }
/* Get an IPA clock reference, but only if the reference count is /* Get an IPA clock reference, but only if the reference count is
...@@ -286,9 +298,9 @@ ipa_clock_init(struct device *dev, const struct ipa_clock_data *data) ...@@ -286,9 +298,9 @@ ipa_clock_init(struct device *dev, const struct ipa_clock_data *data)
goto err_clk_put; goto err_clk_put;
} }
clock->core = clk; clock->core = clk;
clock->interconnect_data = data->interconnect; clock->interconnect_count = data->interconnect_count;
ret = ipa_interconnect_init(clock, dev); ret = ipa_interconnect_init(clock, dev, data->interconnect_data);
if (ret) if (ret)
goto err_kfree; goto err_kfree;
......
...@@ -309,24 +309,30 @@ static struct ipa_mem_data ipa_mem_data = { ...@@ -309,24 +309,30 @@ static struct ipa_mem_data ipa_mem_data = {
.smem_size = 0x00002000, .smem_size = 0x00002000,
}; };
/* Interconnect bandwidths are in 1000 byte/second units */
static struct ipa_interconnect_data ipa_interconnect_data[] = {
{
.name = "memory",
.peak_bandwidth = 465000, /* 465 MBps */
.average_bandwidth = 80000, /* 80 MBps */
},
/* Average bandwidth is unused for the next two interconnects */
{
.name = "imem",
.peak_bandwidth = 68570, /* 68.570 MBps */
.average_bandwidth = 0, /* unused */
},
{
.name = "config",
.peak_bandwidth = 30000, /* 30 MBps */
.average_bandwidth = 0, /* unused */
},
};
static struct ipa_clock_data ipa_clock_data = { static struct ipa_clock_data ipa_clock_data = {
.core_clock_rate = 100 * 1000 * 1000, /* Hz */ .core_clock_rate = 100 * 1000 * 1000, /* Hz */
/* Interconnect rates are in 1000 byte/second units */ .interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect = { .interconnect_data = ipa_interconnect_data,
[IPA_INTERCONNECT_MEMORY] = {
.peak_rate = 465000, /* 465 MBps */
.average_rate = 80000, /* 80 MBps */
},
/* Average rate is unused for the next two interconnects */
[IPA_INTERCONNECT_IMEM] = {
.peak_rate = 68570, /* 68.570 MBps */
.average_rate = 0, /* unused */
},
[IPA_INTERCONNECT_CONFIG] = {
.peak_rate = 30000, /* 30 MBps */
.average_rate = 0, /* unused */
},
},
}; };
/* Configuration data for the SC7180 SoC. */ /* Configuration data for the SC7180 SoC. */
......
...@@ -329,24 +329,30 @@ static struct ipa_mem_data ipa_mem_data = { ...@@ -329,24 +329,30 @@ static struct ipa_mem_data ipa_mem_data = {
.smem_size = 0x00002000, .smem_size = 0x00002000,
}; };
/* Interconnect bandwidths are in 1000 byte/second units */
static struct ipa_interconnect_data ipa_interconnect_data[] = {
{
.name = "memory",
.peak_bandwidth = 600000, /* 600 MBps */
.average_bandwidth = 80000, /* 80 MBps */
},
/* Average bandwidth is unused for the next two interconnects */
{
.name = "imem",
.peak_bandwidth = 350000, /* 350 MBps */
.average_bandwidth = 0, /* unused */
},
{
.name = "config",
.peak_bandwidth = 40000, /* 40 MBps */
.average_bandwidth = 0, /* unused */
},
};
static struct ipa_clock_data ipa_clock_data = { static struct ipa_clock_data ipa_clock_data = {
.core_clock_rate = 75 * 1000 * 1000, /* Hz */ .core_clock_rate = 75 * 1000 * 1000, /* Hz */
/* Interconnect rates are in 1000 byte/second units */ .interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect = { .interconnect_data = ipa_interconnect_data,
[IPA_INTERCONNECT_MEMORY] = {
.peak_rate = 600000, /* 600 MBps */
.average_rate = 80000, /* 80 MBps */
},
/* Average rate is unused for the next two interconnects */
[IPA_INTERCONNECT_IMEM] = {
.peak_rate = 350000, /* 350 MBps */
.average_rate = 0, /* unused */
},
[IPA_INTERCONNECT_CONFIG] = {
.peak_rate = 40000, /* 40 MBps */
.average_rate = 0, /* unused */
},
},
}; };
/* Configuration data for the SDM845 SoC. */ /* Configuration data for the SDM845 SoC. */
......
...@@ -258,32 +258,28 @@ struct ipa_mem_data { ...@@ -258,32 +258,28 @@ struct ipa_mem_data {
u32 smem_size; u32 smem_size;
}; };
/** enum ipa_interconnect_id - IPA interconnect identifier */
enum ipa_interconnect_id {
IPA_INTERCONNECT_MEMORY,
IPA_INTERCONNECT_IMEM,
IPA_INTERCONNECT_CONFIG,
IPA_INTERCONNECT_COUNT, /* Last; not an interconnect */
};
/** /**
* struct ipa_interconnect_data - description of IPA interconnect rates * struct ipa_interconnect_data - description of IPA interconnect bandwidths
* @peak_rate: Peak interconnect bandwidth (in 1000 byte/sec units) * @name: Interconnect name (matches interconnect-name in DT)
* @average_rate: Average interconnect bandwidth (in 1000 byte/sec units) * @peak_bandwidth: Peak interconnect bandwidth (in 1000 byte/sec units)
* @average_bandwidth: Average interconnect bandwidth (in 1000 byte/sec units)
*/ */
struct ipa_interconnect_data { struct ipa_interconnect_data {
u32 peak_rate; const char *name;
u32 average_rate; u32 peak_bandwidth;
u32 average_bandwidth;
}; };
/** /**
* struct ipa_clock_data - description of IPA clock and interconnect rates * struct ipa_clock_data - description of IPA clock and interconnect rates
* @core_clock_rate: Core clock rate (Hz) * @core_clock_rate: Core clock rate (Hz)
* @interconnect: Array of interconnect bandwidth parameters * @interconnect_count: Number of entries in the interconnect_data array
* @interconnect_data: IPA interconnect configuration data
*/ */
struct ipa_clock_data { struct ipa_clock_data {
u32 core_clock_rate; u32 core_clock_rate;
struct ipa_interconnect_data interconnect[IPA_INTERCONNECT_COUNT]; u32 interconnect_count; /* # entries in interconnect_data[] */
const struct ipa_interconnect_data *interconnect_data;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment