On 24.12.2024 11:57 AM, Dmitry Baryshkov wrote:currently 0xff are reserved not sure in future PCIe spec data rates
On Tue, 24 Dec 2024 at 12:36, Krishna Chaitanya Chundru
<quic_krichai@xxxxxxxxxxx> wrote:
On 12/24/2024 3:25 PM, Dmitry Baryshkov wrote:
On Tue, Dec 24, 2024 at 02:47:00PM +0530, Krishna Chaitanya Chundru wrote:in the dwc driver patch I was using pointers and memory allocation
On 12/24/2024 12:00 AM, Dmitry Baryshkov wrote:
On Mon, Dec 23, 2024 at 10:13:29PM +0530, Krishna Chaitanya Chundru wrote:I don't feel having array in the preset structure looks good, I have
On 12/23/2024 8:56 PM, Dmitry Baryshkov wrote:
On Mon, Dec 23, 2024 at 08:02:23PM +0530, Krishna Chaitanya Chundru wrote:ok.
On 12/23/2024 5:17 PM, Dmitry Baryshkov wrote:
On Mon, Dec 23, 2024 at 12:21:15PM +0530, Krishna Chaitanya Chundru wrote:I will update this in next series.
PCIe equalization presets are predefined settings used to optimize
signal integrity by compensating for signal loss and distortion in
high-speed data transmission.
As per PCIe spec 6.0.1 revision section 8.3.3.3 & 4.2.4 for data rates
of 8.0 GT/s, 16.0 GT/s, 32.0 GT/s, and 64.0 GT/s, there is a way to
configure lane equalization presets for each lane to enhance the PCIe
link reliability. Each preset value represents a different combination
of pre-shoot and de-emphasis values. For each data rate, different
registers are defined: for 8.0 GT/s, registers are defined in section
7.7.3.4; for 16.0 GT/s, in section 7.7.5.9, etc. The 8.0 GT/s rate has
an extra receiver preset hint, requiring 16 bits per lane, while the
remaining data rates use 8 bits per lane.
Based on the number of lanes and the supported data rate, this function
reads the device tree property and stores in the presets structure.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@xxxxxxxxxxxxxxxx>
---
drivers/pci/of.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 17 +++++++++++++++--
2 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index dacea3fc5128..99e0e7ae12e9 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -826,3 +826,48 @@ u32 of_pci_get_slot_power_limit(struct device_node *node,
return slot_power_limit_mw;
}
EXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit);
+
kerneldoc? Define who should free the memory and how.
as we are allocating using devm_kzalloc it should be freed on driver
detach, as no special freeing is required.
I was trying iterate over each element on the structure as presets holds the+int of_pci_get_equalization_presets(struct device *dev,
+ struct pci_eq_presets *presets,
+ int num_lanes)
+{
+ char name[20];
+ void **preset;
+ void *temp;
+ int ret;
+
+ if (of_property_present(dev->of_node, "eq-presets-8gts")) {
+ presets->eq_presets_8gts = devm_kzalloc(dev, sizeof(u16) * num_lanes, GFP_KERNEL);
+ if (!presets->eq_presets_8gts)
+ return -ENOMEM;
+
+ ret = of_property_read_u16_array(dev->of_node, "eq-presets-8gts",
+ presets->eq_presets_8gts, num_lanes);
+ if (ret) {
+ dev_err(dev, "Error reading eq-presets-8gts %d\n", ret);
+ return ret;
+ }
+ }
+
+ for (int i = 1; i < sizeof(struct pci_eq_presets) / sizeof(void *); i++) {
+ snprintf(name, sizeof(name), "eq-presets-%dgts", 8 << i);
+ if (of_property_present(dev->of_node, name)) {
+ temp = devm_kzalloc(dev, sizeof(u8) * num_lanes, GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ ret = of_property_read_u8_array(dev->of_node, name,
+ temp, num_lanes);
+ if (ret) {
+ dev_err(dev, "Error %s %d\n", name, ret);
+ return ret;
+ }
+
+ preset = (void **)((u8 *)presets + i * sizeof(void *));
Ugh.
starting address of the structure and to that we are adding size of the void
* point to go to each element. I did this way to reduce the
redundant code to read all the gts which has same way of storing the data
from the device tree. I will add comments here in the next series.
Please rewrite this in a cleaner way. The code shouldn't raise
questions.
To have common parsing logic I moved them to void*, as these are pointers+ *preset = temp;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pci_get_equalization_presets);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 14d00ce45bfa..82362d58bedc 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -731,7 +731,12 @@ static inline u64 pci_rebar_size_to_bytes(int size)
}
struct device_node;
-
+struct pci_eq_presets {
+ void *eq_presets_8gts;
+ void *eq_presets_16gts;
+ void *eq_presets_32gts;
+ void *eq_presets_64gts;
Why are all of those void*? 8gts is u16*, all other are u8*.
actual memory is allocated by of_pci_get_equalization_presets()
based upon the gts these should not give any issues.
Please, don't. They have types. void pointers are for the opaque data.
I think then better to use v1 patch
https://lore.kernel.org/all/20241116-presets-v1-2-878a837a4fee@xxxxxxxxxxx/
konrad, any objection on using v1 as that will be cleaner way even if we
have some repetitive code.
Konrad had a nice suggestion about using the array of values. Please use
such an array for 16gts and above. This removes most of repetitive code.
come up with this logic if you feel it is not so good I will go to the
suggested way by having array for 16gts and above.
if (of_property_present(dev->of_node, "eq-presets-8gts")) {
presets->eq_presets_8gts = devm_kzalloc(dev, sizeof(u16) *
num_lanes, GFP_KERNEL);
if (!presets->eq_presets_8gts)
return -ENOMEM;
ret = of_property_read_u16_array(dev->of_node,
"eq-presets-8gts",
presets->eq_presets_8gts, num_lanes);
if (ret) {
dev_err(dev, "Error reading eq-presets-8gts %d\n",
ret);
return ret;
}
}
for (int i = EQ_PRESET_TYPE_16GTS; i < EQ_PRESET_TYPE_64GTS; i++) {
snprintf(name, sizeof(name), "eq-presets-%dgts", 8 << i);
if (of_property_present(dev->of_node, name)) {
temp = devm_kzalloc(dev, sizeof(u8) * num_lanes,
GFP_KERNEL);
if (!temp)
return -ENOMEM;
ret = of_property_read_u8_array(dev->of_node, name,
temp, num_lanes);
if (ret) {
dev_err(dev, "Error %s %d\n", name, ret);
return ret;
}
switch (i) {
case EQ_PRESET_TYPE_16GTS:
presets->eq_presets_16gts = temp;
break;
case EQ_PRESET_TYPE_32GTS:
presets->eq_presets_32gts = temp;
break;
case EQ_PRESET_TYPE_64GTS:
presets->eq_presets_64gts = temp;
break;
}
This looks like 'presets->eq_presets[i] = temp;', but I won't insist on
that.
Also, a strange thought came to my mind: we know that there won't be
more than 16 lanes. Can we have the following structure instead:
#define MAX_LANES 16
enum pcie_gts {
PCIE_GTS_16GTS,
PCIE_GTS_32GTS,
PCIE_GTS_64GTS,
PCIE_GTS_MAX,
};
struct pci_eq_presets {
u16 eq_presets_8gts[MAX_LANES];
u8 eq_presets_Ngts[PCIE_GTS_MAX][MAX_LANES];
};
This should allow you to drop the of_property_present() and
devm_kzalloc(). Just read DT data into a corresponding array.
to known if the property is present or not. If I use this way I might
end up reading dt property again.
Add foo_valid flags to the struct.
Some(u8)/None would be fitting, but we're not there yet :(
Are all 0x00-0xff(ff) values valid for these presets?
I think better to switch to have a
array for above 16gts.
Whichever way works for you.
Sorta-answering the earlier email, I have no concerns either
Konrad