Import of kernel-5.14.0-611.13.1.el9_7

This commit is contained in:
almalinux-bot-kernel 2025-12-13 08:40:25 +00:00
parent 1e764b8f70
commit e239c331eb
73 changed files with 7392 additions and 32 deletions

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dpll/dpll-device.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Digital Phase-Locked Loop (DPLL) Device
maintainers:
- Ivan Vecera <ivecera@redhat.com>
description:
Digital Phase-Locked Loop (DPLL) device is used for precise clock
synchronization in networking and telecom hardware. The device can
have one or more channels (DPLLs) and one or more physical input and
output pins. Each DPLL channel can either produce pulse-per-clock signal
or drive ethernet equipment clock. The type of each channel can be
indicated by dpll-types property.
properties:
$nodename:
pattern: "^dpll(@.*)?$"
"#address-cells":
const: 0
"#size-cells":
const: 0
dpll-types:
description: List of DPLL channel types, one per DPLL instance.
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
items:
enum: [pps, eec]
input-pins:
type: object
description: DPLL input pins
unevaluatedProperties: false
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^pin@[0-9a-f]+$":
$ref: /schemas/dpll/dpll-pin.yaml
unevaluatedProperties: false
required:
- "#address-cells"
- "#size-cells"
output-pins:
type: object
description: DPLL output pins
unevaluatedProperties: false
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^pin@[0-9]+$":
$ref: /schemas/dpll/dpll-pin.yaml
unevaluatedProperties: false
required:
- "#address-cells"
- "#size-cells"
additionalProperties: true

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dpll/dpll-pin.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: DPLL Pin
maintainers:
- Ivan Vecera <ivecera@redhat.com>
description: |
The DPLL pin is either a physical input or output pin that is provided
by a DPLL( Digital Phase-Locked Loop) device. The pin is identified by
its physical order number that is stored in reg property and can have
an additional set of properties like supported (allowed) frequencies,
label, type and may support embedded sync.
Note that the pin in this context has nothing to do with pinctrl.
properties:
reg:
description: Hardware index of the DPLL pin.
maxItems: 1
connection-type:
description: Connection type of the pin
$ref: /schemas/types.yaml#/definitions/string
enum: [ext, gnss, int, mux, synce]
esync-control:
description: Indicates whether the pin supports embedded sync functionality.
type: boolean
label:
description: String exposed as the pin board label
$ref: /schemas/types.yaml#/definitions/string
supported-frequencies-hz:
description: List of supported frequencies for this pin, expressed in Hz.
required:
- reg
additionalProperties: false

View File

@ -0,0 +1,115 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dpll/microchip,zl30731.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Azurite DPLL device
maintainers:
- Ivan Vecera <ivecera@redhat.com>
description:
Microchip Azurite DPLL (ZL3073x) is a family of DPLL devices that
provides up to 5 independent DPLL channels, up to 10 differential or
single-ended inputs and 10 differential or 20 single-ended outputs.
These devices support both I2C and SPI interfaces.
properties:
compatible:
enum:
- microchip,zl30731
- microchip,zl30732
- microchip,zl30733
- microchip,zl30734
- microchip,zl30735
reg:
maxItems: 1
required:
- compatible
- reg
allOf:
- $ref: /schemas/dpll/dpll-device.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
dpll@70 {
compatible = "microchip,zl30732";
reg = <0x70>;
dpll-types = "pps", "eec";
input-pins {
#address-cells = <1>;
#size-cells = <0>;
pin@0 { /* REF0P */
reg = <0>;
connection-type = "ext";
label = "Input 0";
supported-frequencies-hz = /bits/ 64 <1 1000>;
};
};
output-pins {
#address-cells = <1>;
#size-cells = <0>;
pin@3 { /* OUT1N */
reg = <3>;
connection-type = "gnss";
esync-control;
label = "Output 1";
supported-frequencies-hz = /bits/ 64 <1 10000>;
};
};
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
dpll@70 {
compatible = "microchip,zl30731";
reg = <0x70>;
spi-max-frequency = <12500000>;
dpll-types = "pps";
input-pins {
#address-cells = <1>;
#size-cells = <0>;
pin@0 { /* REF0P */
reg = <0>;
connection-type = "ext";
label = "Input 0";
supported-frequencies-hz = /bits/ 64 <1 1000>;
};
};
output-pins {
#address-cells = <1>;
#size-cells = <0>;
pin@3 { /* OUT1N */
reg = <3>;
connection-type = "gnss";
esync-control;
label = "Output 1";
supported-frequencies-hz = /bits/ 64 <1 10000>;
};
};
};
};
...

View File

@ -96,3 +96,4 @@ parameters, info versions, and other features it supports.
prestera
iosm
sfc
zl3073x

View File

@ -0,0 +1,65 @@
.. SPDX-License-Identifier: GPL-2.0
=======================
zl3073x devlink support
=======================
This document describes the devlink features implemented by the ``zl3073x``
device driver.
Parameters
==========
.. list-table:: Generic parameters implemented
:widths: 5 5 90
* - Name
- Mode
- Notes
* - ``clock_id``
- driverinit
- Set the clock ID that is used by the driver for registering DPLL devices
and pins.
Info versions
=============
The ``zl3073x`` driver reports the following versions
.. list-table:: devlink info versions implemented
:widths: 5 5 5 90
* - Name
- Type
- Example
- Description
* - ``asic.id``
- fixed
- 1E94
- Chip identification number
* - ``asic.rev``
- fixed
- 300
- Chip revision number
* - ``fw``
- running
- 7006
- Firmware version number
* - ``custom_cfg``
- running
- 1.3.0.1
- Device configuration version customized by OEM
Flash Update
============
The ``zl3073x`` driver implements support for flash update using the
``devlink-flash`` interface. It supports updating the device flash using a
combined flash image ("bundle") that contains multiple components (firmware
parts and configurations).
During the flash procedure, the standard firmware interface is not available,
so the driver unregisters all DPLLs and associated pins, and re-registers them
once the flash procedure is complete.
The driver does not support any overwrite mask flags.

View File

@ -5946,6 +5946,8 @@ M: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
M: Jiri Pirko <jiri@resnulli.us>
L: netdev@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/dpll/dpll-device.yaml
F: Documentation/devicetree/bindings/dpll/dpll-pin.yaml
F: Documentation/driver-api/dpll.rst
F: drivers/dpll/*
F: include/linux/dpll.h
@ -12924,6 +12926,14 @@ L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/net/wireless/microchip/wilc1000/
MICROCHIP ZL3073X DRIVER
M: Ivan Vecera <ivecera@redhat.com>
M: Prathosh Satish <Prathosh.Satish@microchip.com>
L: netdev@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml
F: drivers/dpll/zl3073x/
MICROSEMI MIPS SOCS
M: Alexandre Belloni <alexandre.belloni@bootlin.com>
M: UNGLinuxDriver@microchip.com

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 7
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 611.11.1
RHEL_RELEASE = 611.13.1
#
# ZSTREAM

View File

@ -462,6 +462,195 @@ void hv_ivm_msr_read(u64 msr, u64 *value)
hv_ghcb_msr_read(msr, value);
}
/*
* Keep track of the PFN regions which were shared with the host. The access
* must be revoked upon kexec/kdump (see hv_ivm_clear_host_access()).
*/
struct hv_enc_pfn_region {
struct list_head list;
u64 pfn;
int count;
};
static LIST_HEAD(hv_list_enc);
static DEFINE_RAW_SPINLOCK(hv_list_enc_lock);
static int hv_list_enc_add(const u64 *pfn_list, int count)
{
struct hv_enc_pfn_region *ent;
unsigned long flags;
u64 pfn;
int i;
for (i = 0; i < count; i++) {
pfn = pfn_list[i];
raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
/* Check if the PFN already exists in some region first */
list_for_each_entry(ent, &hv_list_enc, list) {
if ((ent->pfn <= pfn) && (ent->pfn + ent->count - 1 >= pfn))
/* Nothing to do - pfn is already in the list */
goto unlock_done;
}
/*
* Check if the PFN is adjacent to an existing region. Growing
* a region can make it adjacent to another one but merging is
* not (yet) implemented for simplicity. A PFN cannot be added
* to two regions to keep the logic in hv_list_enc_remove()
* correct.
*/
list_for_each_entry(ent, &hv_list_enc, list) {
if (ent->pfn + ent->count == pfn) {
/* Grow existing region up */
ent->count++;
goto unlock_done;
} else if (pfn + 1 == ent->pfn) {
/* Grow existing region down */
ent->pfn--;
ent->count++;
goto unlock_done;
}
}
raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
/* No adjacent region found -- create a new one */
ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
if (!ent)
return -ENOMEM;
ent->pfn = pfn;
ent->count = 1;
raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
list_add(&ent->list, &hv_list_enc);
unlock_done:
raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
}
return 0;
}
static int hv_list_enc_remove(const u64 *pfn_list, int count)
{
struct hv_enc_pfn_region *ent, *t;
struct hv_enc_pfn_region new_region;
unsigned long flags;
u64 pfn;
int i;
for (i = 0; i < count; i++) {
pfn = pfn_list[i];
raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
list_for_each_entry_safe(ent, t, &hv_list_enc, list) {
if (pfn == ent->pfn + ent->count - 1) {
/* Removing tail pfn */
ent->count--;
if (!ent->count) {
list_del(&ent->list);
kfree(ent);
}
goto unlock_done;
} else if (pfn == ent->pfn) {
/* Removing head pfn */
ent->count--;
ent->pfn++;
if (!ent->count) {
list_del(&ent->list);
kfree(ent);
}
goto unlock_done;
} else if (pfn > ent->pfn && pfn < ent->pfn + ent->count - 1) {
/*
* Removing a pfn in the middle. Cut off the tail
* of the existing region and create a template for
* the new one.
*/
new_region.pfn = pfn + 1;
new_region.count = ent->count - (pfn - ent->pfn + 1);
ent->count = pfn - ent->pfn;
goto unlock_split;
}
}
unlock_done:
raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
continue;
unlock_split:
raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
if (!ent)
return -ENOMEM;
ent->pfn = new_region.pfn;
ent->count = new_region.count;
raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
list_add(&ent->list, &hv_list_enc);
raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
}
return 0;
}
/* Stop new private<->shared conversions */
static void hv_vtom_kexec_begin(void)
{
if (!IS_ENABLED(CONFIG_KEXEC_CORE))
return;
/*
* Crash kernel reaches here with interrupts disabled: can't wait for
* conversions to finish.
*
* If race happened, just report and proceed.
*/
if (!set_memory_enc_stop_conversion())
pr_warn("Failed to stop shared<->private conversions\n");
}
static void hv_vtom_kexec_finish(void)
{
struct hv_gpa_range_for_visibility *input;
struct hv_enc_pfn_region *ent;
unsigned long flags;
u64 hv_status;
int cur, i;
local_irq_save(flags);
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
if (unlikely(!input))
goto out;
list_for_each_entry(ent, &hv_list_enc, list) {
for (i = 0, cur = 0; i < ent->count; i++) {
input->gpa_page_list[cur] = ent->pfn + i;
cur++;
if (cur == HV_MAX_MODIFY_GPA_REP_COUNT || i == ent->count - 1) {
input->partition_id = HV_PARTITION_ID_SELF;
input->host_visibility = VMBUS_PAGE_NOT_VISIBLE;
input->reserved0 = 0;
input->reserved1 = 0;
hv_status = hv_do_rep_hypercall(
HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY,
cur, 0, input, NULL);
WARN_ON_ONCE(!hv_result_success(hv_status));
cur = 0;
}
}
}
out:
local_irq_restore(flags);
}
/*
* hv_mark_gpa_visibility - Set pages visible to host via hvcall.
*
@ -475,6 +664,7 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
struct hv_gpa_range_for_visibility *input;
u64 hv_status;
unsigned long flags;
int ret;
/* no-op if partition isolation is not enabled */
if (!hv_is_isolation_supported())
@ -486,6 +676,13 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
return -EINVAL;
}
if (visibility == VMBUS_PAGE_NOT_VISIBLE)
ret = hv_list_enc_remove(pfn, count);
else
ret = hv_list_enc_add(pfn, count);
if (ret)
return ret;
local_irq_save(flags);
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
@ -506,8 +703,18 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
if (hv_result_success(hv_status))
return 0;
if (visibility == VMBUS_PAGE_NOT_VISIBLE)
ret = hv_list_enc_add(pfn, count);
else
return -EFAULT;
ret = hv_list_enc_remove(pfn, count);
/*
* There's no good way to recover from -ENOMEM here, the accounting is
* wrong either way.
*/
WARN_ON_ONCE(ret);
return -EFAULT;
}
/*
@ -669,6 +876,8 @@ void __init hv_vtom_init(void)
x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
x86_platform.guest.enc_status_change_prepare = hv_vtom_clear_present;
x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
x86_platform.guest.enc_kexec_begin = hv_vtom_kexec_begin;
x86_platform.guest.enc_kexec_finish = hv_vtom_kexec_finish;
/* Set WB as the default cache mode. */
mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);

View File

@ -4093,6 +4093,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7269,7 +7278,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
CONFIG_HTE_TEGRA194_TEST=m
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4076,6 +4076,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7246,7 +7255,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
# CONFIG_HTE_TEGRA194_TEST is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4097,6 +4097,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7274,7 +7283,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
CONFIG_HTE_TEGRA194_TEST=m
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4096,6 +4096,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7273,7 +7282,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
CONFIG_HTE_TEGRA194_TEST=m
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4080,6 +4080,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7251,7 +7260,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
# CONFIG_HTE_TEGRA194_TEST is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4098,6 +4098,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7276,7 +7285,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
CONFIG_HTE_TEGRA194_TEST=m
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4082,6 +4082,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7254,7 +7263,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
# CONFIG_HTE_TEGRA194_TEST is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -4080,6 +4080,15 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
@ -7251,7 +7260,6 @@ CONFIG_INTERCONNECT_IMX8MP=m
CONFIG_HTE=y
CONFIG_HTE_TEGRA194=m
# CONFIG_HTE_TEGRA194_TEST is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3325,6 +3325,14 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
# end of DPLL device support
# CONFIG_PINCTRL is not set
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
@ -5679,7 +5687,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3342,6 +3342,14 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
# end of DPLL device support
# CONFIG_PINCTRL is not set
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
@ -5696,7 +5704,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3345,6 +3345,14 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
# end of DPLL device support
# CONFIG_PINCTRL is not set
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
@ -5693,7 +5701,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3328,6 +3328,14 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
# end of DPLL device support
# CONFIG_PINCTRL is not set
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
@ -5676,7 +5684,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -2400,6 +2400,13 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
# CONFIG_ZL3073X_I2C is not set
# end of DPLL device support
# CONFIG_PINCTRL is not set
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@ -2979,7 +2986,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -1002,6 +1002,11 @@ CONFIG_PTP_1588_CLOCK_OPTIONAL=y
# CONFIG_PTP_1588_CLOCK_MOCK is not set
# end of PTP clock support
#
# DPLL device support
#
# end of DPLL device support
# CONFIG_PINCTRL is not set
# CONFIG_GPIOLIB is not set
CONFIG_POWER_RESET=y

View File

@ -2423,6 +2423,13 @@ CONFIG_PTP_1588_CLOCK_MOCK=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
# CONFIG_ZL3073X_I2C is not set
# end of DPLL device support
# CONFIG_PINCTRL is not set
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@ -3002,7 +3009,6 @@ CONFIG_NVMEM_SYSFS=y
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3934,7 +3934,7 @@ CONFIG_I2C_MUX=m
# CONFIG_I2C_MUX_GPIO is not set
# CONFIG_I2C_MUX_LTC4306 is not set
# CONFIG_I2C_MUX_PCA9541 is not set
# CONFIG_I2C_MUX_PCA954x is not set
CONFIG_I2C_MUX_PCA954x=m
# CONFIG_I2C_MUX_REG is not set
CONFIG_I2C_MUX_MLXCPLD=m
# end of Multiplexer I2C Chip support
@ -4092,6 +4092,15 @@ CONFIG_PTP_1588_CLOCK_VMW=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_PINMUX=y
CONFIG_PINCONF=y
@ -7970,7 +7979,6 @@ CONFIG_COUNTER=m
CONFIG_INTEL_QEP=m
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3940,7 +3940,7 @@ CONFIG_I2C_MUX=m
# CONFIG_I2C_MUX_GPIO is not set
# CONFIG_I2C_MUX_LTC4306 is not set
# CONFIG_I2C_MUX_PCA9541 is not set
# CONFIG_I2C_MUX_PCA954x is not set
CONFIG_I2C_MUX_PCA954x=m
# CONFIG_I2C_MUX_REG is not set
CONFIG_I2C_MUX_MLXCPLD=m
# end of Multiplexer I2C Chip support
@ -4098,6 +4098,15 @@ CONFIG_PTP_1588_CLOCK_VMW=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_PINMUX=y
CONFIG_PINCONF=y
@ -8022,7 +8031,6 @@ CONFIG_COUNTER=m
CONFIG_INTEL_QEP=m
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3924,7 +3924,7 @@ CONFIG_I2C_MUX=m
# CONFIG_I2C_MUX_GPIO is not set
# CONFIG_I2C_MUX_LTC4306 is not set
# CONFIG_I2C_MUX_PCA9541 is not set
# CONFIG_I2C_MUX_PCA954x is not set
CONFIG_I2C_MUX_PCA954x=m
# CONFIG_I2C_MUX_REG is not set
CONFIG_I2C_MUX_MLXCPLD=m
# end of Multiplexer I2C Chip support
@ -4082,6 +4082,15 @@ CONFIG_PTP_1588_CLOCK_VMW=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_PINMUX=y
CONFIG_PINCONF=y
@ -7995,7 +8004,6 @@ CONFIG_COUNTER=m
CONFIG_INTEL_QEP=m
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -3917,7 +3917,7 @@ CONFIG_I2C_MUX=m
# CONFIG_I2C_MUX_GPIO is not set
# CONFIG_I2C_MUX_LTC4306 is not set
# CONFIG_I2C_MUX_PCA9541 is not set
# CONFIG_I2C_MUX_PCA954x is not set
CONFIG_I2C_MUX_PCA954x=m
# CONFIG_I2C_MUX_REG is not set
CONFIG_I2C_MUX_MLXCPLD=m
# end of Multiplexer I2C Chip support
@ -4075,6 +4075,15 @@ CONFIG_PTP_1588_CLOCK_VMW=m
# CONFIG_PTP_1588_CLOCK_OCP is not set
# end of PTP clock support
#
# DPLL device support
#
CONFIG_DPLL=y
CONFIG_ZL3073X=m
CONFIG_ZL3073X_I2C=m
CONFIG_ZL3073X_SPI=m
# end of DPLL device support
CONFIG_PINCTRL=y
CONFIG_PINMUX=y
CONFIG_PINCONF=y
@ -7943,7 +7952,6 @@ CONFIG_COUNTER=m
CONFIG_INTEL_QEP=m
# CONFIG_MOST is not set
# CONFIG_HTE is not set
CONFIG_DPLL=y
# end of Device Drivers
#

View File

@ -75,6 +75,8 @@ source "drivers/pps/Kconfig"
source "drivers/ptp/Kconfig"
source "drivers/dpll/Kconfig"
source "drivers/pinctrl/Kconfig"
source "drivers/gpio/Kconfig"
@ -245,6 +247,4 @@ source "drivers/most/Kconfig"
source "drivers/hte/Kconfig"
source "drivers/dpll/Kconfig"
endmenu

View File

@ -3,5 +3,11 @@
# Generic DPLL drivers configuration
#
menu "DPLL device support"
config DPLL
bool
source "drivers/dpll/zl3073x/Kconfig"
endmenu

View File

@ -7,3 +7,5 @@ obj-$(CONFIG_DPLL) += dpll.o
dpll-y += dpll_core.o
dpll-y += dpll_netlink.o
dpll-y += dpll_nl.o
obj-$(CONFIG_ZL3073X) += zl3073x/

View File

@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0-only
config ZL3073X
tristate "Microchip Azurite DPLL/PTP/SyncE devices" if COMPILE_TEST
depends on NET
select DPLL
select NET_DEVLINK
select REGMAP
help
This driver supports Microchip Azurite family DPLL/PTP/SyncE
devices that support up to 5 independent DPLL channels,
10 input pins and up to 20 output pins.
To compile this driver as a module, choose M here. The module
will be called zl3073x.
config ZL3073X_I2C
tristate "I2C bus implementation for Microchip Azurite devices"
depends on I2C && NET
select REGMAP_I2C
select ZL3073X
help
This is I2C bus implementation for Microchip Azurite DPLL/PTP/SyncE
devices.
To compile this driver as a module, choose M here: the module will
be called zl3073x_i2c.
config ZL3073X_SPI
tristate "SPI bus implementation for Microchip Azurite devices"
depends on NET && SPI
select REGMAP_SPI
select ZL3073X
help
This is SPI bus implementation for Microchip Azurite DPLL/PTP/SyncE
devices.
To compile this driver as a module, choose M here: the module will
be called zl3073x_spi.

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ZL3073X) += zl3073x.o
zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o prop.o
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
zl3073x_i2c-objs := i2c.o
obj-$(CONFIG_ZL3073X_SPI) += zl3073x_spi.o
zl3073x_spi-objs := spi.o

1259
drivers/dpll/zl3073x/core.c Normal file

File diff suppressed because it is too large Load Diff

416
drivers/dpll/zl3073x/core.h Normal file
View File

@ -0,0 +1,416 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_CORE_H
#define _ZL3073X_CORE_H
#include <linux/bitfield.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include "regs.h"
struct device;
struct regmap;
struct zl3073x_dpll;
/*
* Hardware limits for ZL3073x chip family
*/
#define ZL3073X_MAX_CHANNELS 5
#define ZL3073X_NUM_REFS 10
#define ZL3073X_NUM_OUTS 10
#define ZL3073X_NUM_SYNTHS 5
#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
ZL3073X_NUM_OUTPUT_PINS)
/**
* struct zl3073x_ref - input reference invariant info
* @enabled: input reference is enabled or disabled
* @diff: true if input reference is differential
* @ffo: current fractional frequency offset
*/
struct zl3073x_ref {
bool enabled;
bool diff;
s64 ffo;
};
/**
* struct zl3073x_out - output invariant info
* @enabled: out is enabled or disabled
* @synth: synthesizer the out is connected to
* @signal_format: out signal format
*/
struct zl3073x_out {
bool enabled;
u8 synth;
u8 signal_format;
};
/**
* struct zl3073x_synth - synthesizer invariant info
* @freq: synthesizer frequency
* @dpll: ID of DPLL the synthesizer is driven by
* @enabled: synth is enabled or disabled
*/
struct zl3073x_synth {
u32 freq;
u8 dpll;
bool enabled;
};
/**
* struct zl3073x_dev - zl3073x device
* @dev: pointer to device
* @regmap: regmap to access device registers
* @multiop_lock: to serialize multiple register operations
* @clock_id: clock id of the device
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
* @dplls: list of DPLLs
* @kworker: thread for periodic work
* @work: periodic work
*/
struct zl3073x_dev {
struct device *dev;
struct regmap *regmap;
struct mutex multiop_lock;
u64 clock_id;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
struct zl3073x_out out[ZL3073X_NUM_OUTS];
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
/* DPLL channels */
struct list_head dplls;
/* Monitor */
struct kthread_worker *kworker;
struct kthread_delayed_work work;
};
struct zl3073x_chip_info {
const u16 *ids;
size_t num_ids;
int num_channels;
};
extern const struct zl3073x_chip_info zl30731_chip_info;
extern const struct zl3073x_chip_info zl30732_chip_info;
extern const struct zl3073x_chip_info zl30733_chip_info;
extern const struct zl3073x_chip_info zl30734_chip_info;
extern const struct zl3073x_chip_info zl30735_chip_info;
extern const struct regmap_config zl3073x_regmap_config;
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
int zl3073x_dev_probe(struct zl3073x_dev *zldev,
const struct zl3073x_chip_info *chip_info);
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
/**********************
* Registers operations
**********************/
/**
* struct zl3073x_hwreg_seq_item - HW register write sequence item
* @addr: HW register to be written
* @value: value to be written to HW register
* @mask: bitmask indicating bits to be updated
* @wait: number of ms to wait after register write
*/
struct zl3073x_hwreg_seq_item {
u32 addr;
u32 value;
u32 mask;
u32 wait;
};
#define HWREG_SEQ_ITEM(_addr, _value, _mask, _wait) \
{ \
.addr = _addr, \
.value = FIELD_PREP_CONST(_mask, _value), \
.mask = _mask, \
.wait = _wait, \
}
int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
unsigned int mask_reg, u16 mask_val);
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask);
int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val);
int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val);
int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val);
int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val);
int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val);
int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val);
int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val);
int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
int zl3073x_read_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 *value);
int zl3073x_write_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value);
int zl3073x_update_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value,
u32 mask);
int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
const struct zl3073x_hwreg_seq_item *seq,
size_t num_items);
/*****************
* Misc operations
*****************/
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
static inline bool
zl3073x_is_n_pin(u8 id)
{
/* P-pins ids are even while N-pins are odd */
return id & 1;
}
static inline bool
zl3073x_is_p_pin(u8 id)
{
return !zl3073x_is_n_pin(id);
}
/**
* zl3073x_input_pin_ref_get - get reference for given input pin
* @id: input pin id
*
* Return: reference id for the given input pin
*/
static inline u8
zl3073x_input_pin_ref_get(u8 id)
{
return id;
}
/**
* zl3073x_output_pin_out_get - get output for the given output pin
* @id: output pin id
*
* Return: output id for the given output pin
*/
static inline u8
zl3073x_output_pin_out_get(u8 id)
{
/* Output pin pair shares the single output */
return id / 2;
}
/**
* zl3073x_ref_ffo_get - get current fractional frequency offset
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: the latest measured fractional frequency offset
*/
static inline s64
zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->ref[index].ffo;
}
/**
* zl3073x_ref_is_diff - check if the given input reference is differential
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: true if reference is differential, false if reference is single-ended
*/
static inline bool
zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
{
return zldev->ref[index].diff;
}
/**
* zl3073x_ref_is_enabled - check if the given input reference is enabled
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: true if input refernce is enabled, false otherwise
*/
static inline bool
zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
return zldev->ref[index].enabled;
}
/**
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
* @zldev: pointer to zl3073x device
* @index: synth index
*
* Return: ID of DPLL the given synthetizer is driven by
*/
static inline u8
zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->synth[index].dpll;
}
/**
* zl3073x_synth_freq_get - get synth current freq
* @zldev: pointer to zl3073x device
* @index: synth index
*
* Return: frequency of given synthetizer
*/
static inline u32
zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->synth[index].freq;
}
/**
* zl3073x_synth_is_enabled - check if the given synth is enabled
* @zldev: pointer to zl3073x device
* @index: synth index
*
* Return: true if synth is enabled, false otherwise
*/
static inline bool
zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
return zldev->synth[index].enabled;
}
/**
* zl3073x_out_synth_get - get synth connected to given output
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: index of synth connected to given output.
*/
static inline u8
zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->out[index].synth;
}
/**
* zl3073x_out_is_enabled - check if the given output is enabled
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: true if the output is enabled, false otherwise
*/
static inline bool
zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
u8 synth;
/* Output is enabled only if associated synth is enabled */
synth = zl3073x_out_synth_get(zldev, index);
if (zl3073x_synth_is_enabled(zldev, synth))
return zldev->out[index].enabled;
return false;
}
/**
* zl3073x_out_signal_format_get - get output signal format
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: signal format of given output
*/
static inline u8
zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->out[index].signal_format;
}
/**
* zl3073x_out_dpll_get - get DPLL ID the output is driven by
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: ID of DPLL the given output is driven by
*/
static inline
u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
{
u8 synth;
/* Get synthesizer connected to given output */
synth = zl3073x_out_synth_get(zldev, index);
/* Return DPLL that drives the synth */
return zl3073x_synth_dpll_get(zldev, synth);
}
/**
* zl3073x_out_is_diff - check if the given output is differential
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: true if output is differential, false if output is single-ended
*/
static inline bool
zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
{
switch (zl3073x_out_signal_format_get(zldev, index)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
return true;
default:
break;
}
return false;
}
/**
* zl3073x_output_pin_is_enabled - check if the given output pin is enabled
* @zldev: pointer to zl3073x device
* @id: output pin id
*
* Checks if the output of the given output pin is enabled and also that
* its signal format also enables the given pin.
*
* Return: true if output pin is enabled, false if output pin is disabled
*/
static inline bool
zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
{
u8 output = zl3073x_output_pin_out_get(id);
/* Check if the whole output is enabled */
if (!zl3073x_out_is_enabled(zldev, output))
return false;
/* Check signal format */
switch (zl3073x_out_signal_format_get(zldev, output)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
/* Both output pins are disabled by signal format */
return false;
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P:
/* Output is one single ended P-pin output */
if (zl3073x_is_n_pin(id))
return false;
break;
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N:
/* Output is one single ended N-pin output */
if (zl3073x_is_p_pin(id))
return false;
break;
default:
/* For other format both pins are enabled */
break;
}
return true;
}
#endif /* _ZL3073X_CORE_H */

View File

@ -0,0 +1,390 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/device.h>
#include <linux/netlink.h>
#include <linux/sprintf.h>
#include <linux/types.h>
#include <net/devlink.h>
#include "core.h"
#include "devlink.h"
#include "dpll.h"
#include "flash.h"
#include "fw.h"
#include "regs.h"
/**
* zl3073x_devlink_info_get - Devlink device info callback
* @devlink: devlink structure pointer
* @req: devlink request pointer to store information
* @extack: netlink extack pointer to report errors
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct zl3073x_dev *zldev = devlink_priv(devlink);
u16 id, revision, fw_ver;
char buf[16];
u32 cfg_ver;
int rc;
rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id);
if (rc)
return rc;
snprintf(buf, sizeof(buf), "%X", id);
rc = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
buf);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
if (rc)
return rc;
snprintf(buf, sizeof(buf), "%X", revision);
rc = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
buf);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver);
if (rc)
return rc;
snprintf(buf, sizeof(buf), "%u", fw_ver);
rc = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
buf);
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver);
if (rc)
return rc;
/* No custom config version */
if (cfg_ver == U32_MAX)
return 0;
snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu",
FIELD_GET(GENMASK(31, 24), cfg_ver),
FIELD_GET(GENMASK(23, 16), cfg_ver),
FIELD_GET(GENMASK(15, 8), cfg_ver),
FIELD_GET(GENMASK(7, 0), cfg_ver));
return devlink_info_version_running_put(req, "custom_cfg", buf);
}
static int
zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
struct netlink_ext_ack *extack)
{
struct zl3073x_dev *zldev = devlink_priv(devlink);
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
return -EOPNOTSUPP;
/* Stop normal operation */
zl3073x_dev_stop(zldev);
return 0;
}
static int
zl3073x_devlink_reload_up(struct devlink *devlink,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
u32 *actions_performed,
struct netlink_ext_ack *extack)
{
struct zl3073x_dev *zldev = devlink_priv(devlink);
union devlink_param_value val;
int rc;
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
return -EOPNOTSUPP;
rc = devl_param_driverinit_value_get(devlink,
DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
&val);
if (rc)
return rc;
if (zldev->clock_id != val.vu64) {
dev_dbg(zldev->dev,
"'clock_id' changed to %016llx\n", val.vu64);
zldev->clock_id = val.vu64;
}
/* Restart normal operation */
rc = zl3073x_dev_start(zldev, false);
if (rc)
dev_warn(zldev->dev, "Failed to re-start normal operation\n");
*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
return 0;
}
void zl3073x_devlink_flash_notify(struct zl3073x_dev *zldev, const char *msg,
const char *component, u32 done, u32 total)
{
struct devlink *devlink = priv_to_devlink(zldev);
devlink_flash_update_status_notify(devlink, msg, component, done,
total);
}
/**
* zl3073x_devlink_flash_prepare - Prepare and enter flash mode
* @zldev: zl3073x device pointer
* @zlfw: pointer to loaded firmware
* @extack: netlink extack pointer to report errors
*
* The function stops normal operation and switches the device to flash mode.
* If an error occurs the normal operation is resumed.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_devlink_flash_prepare(struct zl3073x_dev *zldev,
struct zl3073x_fw *zlfw,
struct netlink_ext_ack *extack)
{
struct zl3073x_fw_component *util;
int rc;
util = zlfw->component[ZL_FW_COMPONENT_UTIL];
if (!util) {
zl3073x_devlink_flash_notify(zldev,
"Utility is missing in firmware",
NULL, 0, 0);
return -ENOEXEC;
}
/* Stop normal operation prior entering flash mode */
zl3073x_dev_stop(zldev);
rc = zl3073x_flash_mode_enter(zldev, util->data, util->size, extack);
if (rc) {
zl3073x_devlink_flash_notify(zldev,
"Failed to enter flash mode",
NULL, 0, 0);
/* Resume normal operation */
zl3073x_dev_start(zldev, true);
return rc;
}
return 0;
}
/**
* zl3073x_devlink_flash_finish - Leave flash mode and resume normal operation
* @zldev: zl3073x device pointer
* @extack: netlink extack pointer to report errors
*
* The function switches the device back to standard mode and resumes normal
* operation.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_devlink_flash_finish(struct zl3073x_dev *zldev,
struct netlink_ext_ack *extack)
{
int rc;
/* Reset device CPU to normal mode */
zl3073x_flash_mode_leave(zldev, extack);
/* Resume normal operation */
rc = zl3073x_dev_start(zldev, true);
if (rc)
zl3073x_devlink_flash_notify(zldev,
"Failed to start normal operation",
NULL, 0, 0);
return rc;
}
/**
* zl3073x_devlink_flash_update - Devlink flash update callback
* @devlink: devlink structure pointer
* @params: flashing parameters pointer
* @extack: netlink extack pointer to report errors
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_devlink_flash_update(struct devlink *devlink,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
struct zl3073x_dev *zldev = devlink_priv(devlink);
struct zl3073x_fw *zlfw;
int rc = 0;
zlfw = zl3073x_fw_load(zldev, params->fw->data, params->fw->size,
extack);
if (IS_ERR(zlfw)) {
zl3073x_devlink_flash_notify(zldev, "Failed to load firmware",
NULL, 0, 0);
rc = PTR_ERR(zlfw);
goto finish;
}
/* Stop normal operation and enter flash mode */
rc = zl3073x_devlink_flash_prepare(zldev, zlfw, extack);
if (rc)
goto finish;
rc = zl3073x_fw_flash(zldev, zlfw, extack);
if (rc) {
zl3073x_devlink_flash_finish(zldev, extack);
goto finish;
}
/* Resume normal mode */
rc = zl3073x_devlink_flash_finish(zldev, extack);
finish:
if (!IS_ERR(zlfw))
zl3073x_fw_free(zlfw);
zl3073x_devlink_flash_notify(zldev,
rc ? "Flashing failed" : "Flashing done",
NULL, 0, 0);
return rc;
}
static const struct devlink_ops zl3073x_devlink_ops = {
.info_get = zl3073x_devlink_info_get,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
.reload_down = zl3073x_devlink_reload_down,
.reload_up = zl3073x_devlink_reload_up,
.flash_update = zl3073x_devlink_flash_update,
};
static void
zl3073x_devlink_free(void *ptr)
{
devlink_free(ptr);
}
/**
* zl3073x_devm_alloc - allocates zl3073x device structure
* @dev: pointer to device structure
*
* Allocates zl3073x device structure as device resource.
*
* Return: pointer to zl3073x device on success, error pointer on error
*/
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
{
struct zl3073x_dev *zldev;
struct devlink *devlink;
int rc;
devlink = devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev);
if (!devlink)
return ERR_PTR(-ENOMEM);
/* Add devres action to free devlink device */
rc = devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink);
if (rc)
return ERR_PTR(rc);
zldev = devlink_priv(devlink);
zldev->dev = dev;
dev_set_drvdata(zldev->dev, zldev);
return zldev;
}
EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, ZL3073X);
static int
zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack)
{
if (!val.vu64) {
NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero");
return -EINVAL;
}
return 0;
}
static const struct devlink_param zl3073x_devlink_params[] = {
DEVLINK_PARAM_GENERIC(CLOCK_ID, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
NULL, NULL,
zl3073x_devlink_param_clock_id_validate),
};
static void
zl3073x_devlink_unregister(void *ptr)
{
struct devlink *devlink = priv_to_devlink(ptr);
devl_lock(devlink);
/* Unregister devlink params */
devl_params_unregister(devlink, zl3073x_devlink_params,
ARRAY_SIZE(zl3073x_devlink_params));
/* Unregister devlink instance */
devl_unregister(devlink);
devl_unlock(devlink);
}
/**
* zl3073x_devlink_register - register devlink instance and params
* @zldev: zl3073x device to register the devlink for
*
* Register the devlink instance and parameters associated with the device.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_devlink_register(struct zl3073x_dev *zldev)
{
struct devlink *devlink = priv_to_devlink(zldev);
union devlink_param_value value;
int rc;
devl_lock(devlink);
/* Register devlink params */
rc = devl_params_register(devlink, zl3073x_devlink_params,
ARRAY_SIZE(zl3073x_devlink_params));
if (rc) {
devl_unlock(devlink);
return rc;
}
value.vu64 = zldev->clock_id;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
value);
/* Register devlink instance */
devl_register(devlink);
devl_unlock(devlink);
/* Add devres action to unregister devlink device */
return devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister,
zldev);
}

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_DEVLINK_H
#define _ZL3073X_DEVLINK_H
struct zl3073x_dev;
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
int zl3073x_devlink_register(struct zl3073x_dev *zldev);
void zl3073x_devlink_flash_notify(struct zl3073x_dev *zldev, const char *msg,
const char *component, u32 done, u32 total);
#endif /* _ZL3073X_DEVLINK_H */

2318
drivers/dpll/zl3073x/dpll.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_DPLL_H
#define _ZL3073X_DPLL_H
#include <linux/dpll.h>
#include <linux/list.h>
#include "core.h"
/**
* struct zl3073x_dpll - ZL3073x DPLL sub-device structure
* @list: this DPLL list entry
* @dev: pointer to multi-function parent device
* @id: DPLL index
* @refsel_mode: reference selection mode
* @forced_ref: selected reference in forced reference lock mode
* @check_count: periodic check counter
* @phase_monitor: is phase offset monitor enabled
* @dpll_dev: pointer to registered DPLL device
* @lock_status: last saved DPLL lock status
* @pins: list of pins
*/
struct zl3073x_dpll {
struct list_head list;
struct zl3073x_dev *dev;
u8 id;
u8 refsel_mode;
u8 forced_ref;
u8 check_count;
bool phase_monitor;
struct dpll_device *dpll_dev;
enum dpll_lock_status lock_status;
struct list_head pins;
};
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
void zl3073x_dpll_free(struct zl3073x_dpll *zldpll);
int zl3073x_dpll_register(struct zl3073x_dpll *zldpll);
void zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll);
int zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev);
void zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll);
#endif /* _ZL3073X_DPLL_H */

View File

@ -0,0 +1,666 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/minmax.h>
#include <linux/netlink.h>
#include <linux/sched/signal.h>
#include <linux/sizes.h>
#include <linux/sprintf.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#include <net/devlink.h>
#include "core.h"
#include "devlink.h"
#include "flash.h"
#define ZL_FLASH_ERR_PFX "FW update failed: "
#define ZL_FLASH_ERR_MSG(_extack, _msg, ...) \
NL_SET_ERR_MSG_FMT_MOD((_extack), ZL_FLASH_ERR_PFX _msg, \
## __VA_ARGS__)
/**
* zl3073x_flash_download - Download image block to device memory
* @zldev: zl3073x device structure
* @component: name of the component to be downloaded
* @addr: device memory target address
* @data: pointer to data to download
* @size: size of data to download
* @extack: netlink extack pointer to report errors
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_flash_download(struct zl3073x_dev *zldev, const char *component,
u32 addr, const void *data, size_t size,
struct netlink_ext_ack *extack)
{
#define ZL_CHECK_DELAY 5000 /* Check for interrupt each 5 seconds */
unsigned long check_time;
const void *ptr, *end;
int rc = 0;
dev_dbg(zldev->dev, "Downloading %zu bytes to device memory at 0x%0x\n",
size, addr);
check_time = jiffies + msecs_to_jiffies(ZL_CHECK_DELAY);
for (ptr = data, end = data + size; ptr < end; ptr += 4, addr += 4) {
/* Write current word to HW memory */
rc = zl3073x_write_hwreg(zldev, addr,
get_unaligned((u32 *)ptr));
if (rc) {
ZL_FLASH_ERR_MSG(extack,
"failed to write to memory at 0x%0x",
addr);
return rc;
}
if (time_is_before_jiffies(check_time)) {
if (signal_pending(current)) {
ZL_FLASH_ERR_MSG(extack,
"Flashing interrupted");
return -EINTR;
}
check_time = jiffies + msecs_to_jiffies(ZL_CHECK_DELAY);
}
/* Report status each 1 kB block */
if ((ptr - data) % 1024 == 0)
zl3073x_devlink_flash_notify(zldev, "Downloading image",
component, ptr - data,
size);
}
zl3073x_devlink_flash_notify(zldev, "Downloading image", component,
ptr - data, size);
dev_dbg(zldev->dev, "%zu bytes downloaded to device memory\n", size);
return rc;
}
/**
* zl3073x_flash_error_check - Check for flash utility errors
* @zldev: zl3073x device structure
* @extack: netlink extack pointer to report errors
*
* The function checks for errors detected by the flash utility and
* reports them if any were found.
*
* Return: 0 on success, -EIO when errors are detected
*/
static int
zl3073x_flash_error_check(struct zl3073x_dev *zldev,
struct netlink_ext_ack *extack)
{
u32 count, cause;
int rc;
rc = zl3073x_read_u32(zldev, ZL_REG_ERROR_COUNT, &count);
if (rc)
return rc;
else if (!count)
return 0; /* No error */
rc = zl3073x_read_u32(zldev, ZL_REG_ERROR_CAUSE, &cause);
if (rc)
return rc;
/* Report errors */
ZL_FLASH_ERR_MSG(extack,
"utility error occurred: count=%u cause=0x%x", count,
cause);
return -EIO;
}
/**
* zl3073x_flash_wait_ready - Check or wait for utility to be ready to flash
* @zldev: zl3073x device structure
* @timeout_ms: timeout for the waiting
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_flash_wait_ready(struct zl3073x_dev *zldev, unsigned int timeout_ms)
{
#define ZL_FLASH_POLL_DELAY_MS 100
unsigned long timeout;
int rc, i;
dev_dbg(zldev->dev, "Waiting for flashing to be ready\n");
timeout = jiffies + msecs_to_jiffies(timeout_ms);
for (i = 0; time_is_after_jiffies(timeout); i++) {
u8 value;
/* Check for interrupt each 1s */
if (i > 9) {
if (signal_pending(current))
return -EINTR;
i = 0;
}
rc = zl3073x_read_u8(zldev, ZL_REG_WRITE_FLASH, &value);
if (rc)
return rc;
value = FIELD_GET(ZL_WRITE_FLASH_OP, value);
if (value == ZL_WRITE_FLASH_OP_DONE)
return 0; /* Successfully done */
msleep(ZL_FLASH_POLL_DELAY_MS);
}
return -ETIMEDOUT;
}
/**
* zl3073x_flash_cmd_wait - Perform flash operation and wait for finish
* @zldev: zl3073x device structure
* @operation: operation to perform
* @extack: netlink extack pointer to report errors
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
struct netlink_ext_ack *extack)
{
#define ZL_FLASH_PHASE1_TIMEOUT_MS 60000 /* up to 1 minute */
#define ZL_FLASH_PHASE2_TIMEOUT_MS 120000 /* up to 2 minutes */
u8 value;
int rc;
dev_dbg(zldev->dev, "Sending flash command: 0x%x\n", operation);
rc = zl3073x_flash_wait_ready(zldev, ZL_FLASH_PHASE1_TIMEOUT_MS);
if (rc)
return rc;
/* Issue the requested operation */
rc = zl3073x_read_u8(zldev, ZL_REG_WRITE_FLASH, &value);
if (rc)
return rc;
value &= ~ZL_WRITE_FLASH_OP;
value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
if (rc)
return rc;
/* Wait for command completion */
rc = zl3073x_flash_wait_ready(zldev, ZL_FLASH_PHASE2_TIMEOUT_MS);
if (rc)
return rc;
return zl3073x_flash_error_check(zldev, extack);
}
/**
* zl3073x_flash_get_sector_size - Get flash sector size
* @zldev: zl3073x device structure
* @sector_size: sector size returned by the function
*
* The function reads the flash sector size detected by flash utility and
* stores it into @sector_size.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_flash_get_sector_size(struct zl3073x_dev *zldev, size_t *sector_size)
{
u8 flash_info;
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_INFO, &flash_info);
if (rc)
return rc;
switch (FIELD_GET(ZL_FLASH_INFO_SECTOR_SIZE, flash_info)) {
case ZL_FLASH_INFO_SECTOR_4K:
*sector_size = SZ_4K;
break;
case ZL_FLASH_INFO_SECTOR_64K:
*sector_size = SZ_64K;
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
/**
* zl3073x_flash_block - Download and flash memory block
* @zldev: zl3073x device structure
* @component: component name
* @operation: flash operation to perform
* @page: destination flash page
* @addr: device memory address to load data
* @data: pointer to data to be flashed
* @size: size of data
* @extack: netlink extack pointer to report errors
*
* The function downloads the memory block given by the @data pointer and
* the size @size and flashes it into internal memory on flash page @page.
* The internal flash operation performed by the firmware is specified by
* the @operation parameter.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_flash_block(struct zl3073x_dev *zldev, const char *component,
u32 operation, u32 page, u32 addr, const void *data,
size_t size, struct netlink_ext_ack *extack)
{
int rc;
/* Download block to device memory */
rc = zl3073x_flash_download(zldev, component, addr, data, size, extack);
if (rc)
return rc;
/* Set address to flash from */
rc = zl3073x_write_u32(zldev, ZL_REG_IMAGE_START_ADDR, addr);
if (rc)
return rc;
/* Set size of block to flash */
rc = zl3073x_write_u32(zldev, ZL_REG_IMAGE_SIZE, size);
if (rc)
return rc;
/* Set destination page to flash */
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_WRITE, page);
if (rc)
return rc;
/* Set filling pattern */
rc = zl3073x_write_u32(zldev, ZL_REG_FILL_PATTERN, U32_MAX);
if (rc)
return rc;
zl3073x_devlink_flash_notify(zldev, "Flashing image", component, 0,
size);
dev_dbg(zldev->dev, "Flashing %zu bytes to page %u\n", size, page);
/* Execute sectors flash operation */
rc = zl3073x_flash_cmd_wait(zldev, operation, extack);
if (rc)
return rc;
zl3073x_devlink_flash_notify(zldev, "Flashing image", component, size,
size);
return 0;
}
/**
* zl3073x_flash_sectors - Flash sectors
* @zldev: zl3073x device structure
* @component: component name
* @page: destination flash page
* @addr: device memory address to load data
* @data: pointer to data to be flashed
* @size: size of data
* @extack: netlink extack pointer to report errors
*
* The function flashes given @data with size of @size to the internal flash
* memory block starting from page @page. The function uses sector flash
* method and has to take into account the flash sector size reported by
* flashing utility. Input data are spliced into blocks according this
* sector size and each block is flashed separately.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_flash_sectors(struct zl3073x_dev *zldev, const char *component,
u32 page, u32 addr, const void *data, size_t size,
struct netlink_ext_ack *extack)
{
#define ZL_FLASH_MAX_BLOCK_SIZE 0x0001E000
#define ZL_FLASH_PAGE_SIZE 256
size_t max_block_size, block_size, sector_size;
const void *ptr, *end;
int rc;
/* Get flash sector size */
rc = zl3073x_flash_get_sector_size(zldev, &sector_size);
if (rc) {
ZL_FLASH_ERR_MSG(extack, "Failed to get flash sector size");
return rc;
}
/* Determine max block size depending on sector size */
max_block_size = ALIGN_DOWN(ZL_FLASH_MAX_BLOCK_SIZE, sector_size);
for (ptr = data, end = data + size; ptr < end; ptr += block_size) {
char comp_str[32];
block_size = min_t(size_t, max_block_size, end - ptr);
/* Add suffix '-partN' if the requested component size is
* greater than max_block_size.
*/
if (max_block_size < size)
snprintf(comp_str, sizeof(comp_str), "%s-part%zu",
component, (ptr - data) / max_block_size + 1);
else
strscpy(comp_str, component);
/* Flash the memory block */
rc = zl3073x_flash_block(zldev, comp_str,
ZL_WRITE_FLASH_OP_SECTORS, page, addr,
ptr, block_size, extack);
if (rc)
goto finish;
/* Move to next page */
page += block_size / ZL_FLASH_PAGE_SIZE;
}
finish:
zl3073x_devlink_flash_notify(zldev,
rc ? "Flashing failed" : "Flashing done",
component, 0, 0);
return rc;
}
/**
* zl3073x_flash_page - Flash page
* @zldev: zl3073x device structure
* @component: component name
* @page: destination flash page
* @addr: device memory address to load data
* @data: pointer to data to be flashed
* @size: size of data
* @extack: netlink extack pointer to report errors
*
* The function flashes given @data with size of @size to the internal flash
* memory block starting with page @page.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_flash_page(struct zl3073x_dev *zldev, const char *component,
u32 page, u32 addr, const void *data, size_t size,
struct netlink_ext_ack *extack)
{
int rc;
/* Flash the memory block */
rc = zl3073x_flash_block(zldev, component, ZL_WRITE_FLASH_OP_PAGE, page,
addr, data, size, extack);
zl3073x_devlink_flash_notify(zldev,
rc ? "Flashing failed" : "Flashing done",
component, 0, 0);
return rc;
}
/**
* zl3073x_flash_page_copy - Copy flash page
* @zldev: zl3073x device structure
* @component: component name
* @src_page: source page to copy
* @dst_page: destination page
* @extack: netlink extack pointer to report errors
*
* The function copies one flash page specified by @src_page into the flash
* page specified by @dst_page.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_flash_page_copy(struct zl3073x_dev *zldev, const char *component,
u32 src_page, u32 dst_page,
struct netlink_ext_ack *extack)
{
int rc;
/* Set source page to be copied */
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_READ, src_page);
if (rc)
return rc;
/* Set destination page for the copy */
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_WRITE, dst_page);
if (rc)
return rc;
/* Perform copy operation */
rc = zl3073x_flash_cmd_wait(zldev, ZL_WRITE_FLASH_OP_COPY_PAGE, extack);
if (rc)
ZL_FLASH_ERR_MSG(extack, "Failed to copy page %u to page %u",
src_page, dst_page);
return rc;
}
/**
* zl3073x_flash_mode_verify - Check flash utility
* @zldev: zl3073x device structure
*
* Return: 0 if the flash utility is ready, <0 on error
*/
static int
zl3073x_flash_mode_verify(struct zl3073x_dev *zldev)
{
u8 family, release;
u32 hash;
int rc;
rc = zl3073x_read_u32(zldev, ZL_REG_FLASH_HASH, &hash);
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_FAMILY, &family);
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_RELEASE, &release);
if (rc)
return rc;
dev_dbg(zldev->dev,
"Flash utility check: hash 0x%08x, fam 0x%02x, rel 0x%02x\n",
hash, family, release);
/* Return success for correct family */
return (family == 0x21) ? 0 : -ENODEV;
}
static int
zl3073x_flash_host_ctrl_enable(struct zl3073x_dev *zldev)
{
u8 host_ctrl;
int rc;
/* Enable host control */
rc = zl3073x_read_u8(zldev, ZL_REG_HOST_CONTROL, &host_ctrl);
if (rc)
return rc;
host_ctrl |= ZL_HOST_CONTROL_ENABLE;
return zl3073x_write_u8(zldev, ZL_REG_HOST_CONTROL, host_ctrl);
}
/**
* zl3073x_flash_mode_enter - Switch the device to flash mode
* @zldev: zl3073x device structure
* @util_ptr: buffer with flash utility
* @util_size: size of buffer with flash utility
* @extack: netlink extack pointer to report errors
*
* The function prepares and switches the device into flash mode.
*
* The procedure:
* 1) Stop device CPU by specific HW register sequence
* 2) Download flash utility to device memory
* 3) Resume device CPU by specific HW register sequence
* 4) Check communication with flash utility
* 5) Enable host control necessary to access flash API
* 6) Check for potential error detected by the utility
*
* The API provided by normal firmware is not available in flash mode
* so the caller has to ensure that this API is not used in this mode.
*
* After performing flash operation the caller should call
* @zl3073x_flash_mode_leave to return back to normal operation.
*
* Return: 0 on success, <0 on error.
*/
int zl3073x_flash_mode_enter(struct zl3073x_dev *zldev, const void *util_ptr,
size_t util_size, struct netlink_ext_ack *extack)
{
/* Sequence to be written prior utility download */
static const struct zl3073x_hwreg_seq_item pre_seq[] = {
HWREG_SEQ_ITEM(0x80000400, 1, BIT(0), 0),
HWREG_SEQ_ITEM(0x80206340, 1, BIT(4), 0),
HWREG_SEQ_ITEM(0x10000000, 1, BIT(2), 0),
HWREG_SEQ_ITEM(0x10000024, 0x00000001, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10000020, 0x00000001, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10000000, 1, BIT(10), 1000),
};
/* Sequence to be written after utility download */
static const struct zl3073x_hwreg_seq_item post_seq[] = {
HWREG_SEQ_ITEM(0x10400004, 0x000000C0, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10400008, 0x00000000, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10400010, 0x20000000, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10400014, 0x20000004, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10000000, 1, GENMASK(10, 9), 0),
HWREG_SEQ_ITEM(0x10000020, 0x00000000, U32_MAX, 0),
HWREG_SEQ_ITEM(0x10000000, 0, BIT(0), 1000),
};
int rc;
zl3073x_devlink_flash_notify(zldev, "Prepare flash mode", "utility",
0, 0);
/* Execure pre-load sequence */
rc = zl3073x_write_hwreg_seq(zldev, pre_seq, ARRAY_SIZE(pre_seq));
if (rc) {
ZL_FLASH_ERR_MSG(extack, "cannot execute pre-load sequence");
goto error;
}
/* Download utility image to device memory */
rc = zl3073x_flash_download(zldev, "utility", 0x20000000, util_ptr,
util_size, extack);
if (rc) {
ZL_FLASH_ERR_MSG(extack, "cannot download flash utility");
goto error;
}
/* Execute post-load sequence */
rc = zl3073x_write_hwreg_seq(zldev, post_seq, ARRAY_SIZE(post_seq));
if (rc) {
ZL_FLASH_ERR_MSG(extack, "cannot execute post-load sequence");
goto error;
}
/* Check that utility identifies itself correctly */
rc = zl3073x_flash_mode_verify(zldev);
if (rc) {
ZL_FLASH_ERR_MSG(extack, "flash utility check failed");
goto error;
}
/* Enable host control */
rc = zl3073x_flash_host_ctrl_enable(zldev);
if (rc) {
ZL_FLASH_ERR_MSG(extack, "cannot enable host control");
goto error;
}
zl3073x_devlink_flash_notify(zldev, "Flash mode enabled", "utility",
0, 0);
return 0;
error:
zl3073x_flash_mode_leave(zldev, extack);
return rc;
}
/**
* zl3073x_flash_mode_leave - Leave flash mode
* @zldev: zl3073x device structure
* @extack: netlink extack pointer to report errors
*
* The function instructs the device to leave the flash mode and
* to return back to normal operation.
*
* The procedure:
* 1) Set reset flag
* 2) Reset the device CPU by specific HW register sequence
* 3) Wait for the device to be ready
* 4) Check the reset flag was cleared
*
* Return: 0 on success, <0 on error
*/
int zl3073x_flash_mode_leave(struct zl3073x_dev *zldev,
struct netlink_ext_ack *extack)
{
/* Sequence to be written after flash */
static const struct zl3073x_hwreg_seq_item fw_reset_seq[] = {
HWREG_SEQ_ITEM(0x80000404, 1, BIT(0), 0),
HWREG_SEQ_ITEM(0x80000410, 1, BIT(0), 0),
};
u8 reset_status;
int rc;
zl3073x_devlink_flash_notify(zldev, "Leaving flash mode", "utility",
0, 0);
/* Read reset status register */
rc = zl3073x_read_u8(zldev, ZL_REG_RESET_STATUS, &reset_status);
if (rc)
return rc;
/* Set reset bit */
reset_status |= ZL_REG_RESET_STATUS_RESET;
/* Update reset status register */
rc = zl3073x_write_u8(zldev, ZL_REG_RESET_STATUS, reset_status);
if (rc)
return rc;
/* We do not check the return value here as the sequence resets
* the device CPU and the last write always return an error.
*/
zl3073x_write_hwreg_seq(zldev, fw_reset_seq, ARRAY_SIZE(fw_reset_seq));
/* Wait for the device to be ready */
msleep(500);
/* Read again the reset status register */
rc = zl3073x_read_u8(zldev, ZL_REG_RESET_STATUS, &reset_status);
if (rc)
return rc;
/* Check the reset bit was cleared */
if (reset_status & ZL_REG_RESET_STATUS_RESET) {
dev_err(zldev->dev,
"Reset not confirmed after switch to normal mode\n");
return -EINVAL;
}
return 0;
}

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __ZL3073X_FLASH_H
#define __ZL3073X_FLASH_H
#include <linux/types.h>
struct netlink_ext_ack;
struct zl3073x_dev;
int zl3073x_flash_mode_enter(struct zl3073x_dev *zldev, const void *util_ptr,
size_t util_size, struct netlink_ext_ack *extack);
int zl3073x_flash_mode_leave(struct zl3073x_dev *zldev,
struct netlink_ext_ack *extack);
int zl3073x_flash_page(struct zl3073x_dev *zldev, const char *component,
u32 page, u32 addr, const void *data, size_t size,
struct netlink_ext_ack *extack);
int zl3073x_flash_page_copy(struct zl3073x_dev *zldev, const char *component,
u32 src_page, u32 dst_page,
struct netlink_ext_ack *extack);
int zl3073x_flash_sectors(struct zl3073x_dev *zldev, const char *component,
u32 page, u32 addr, const void *data, size_t size,
struct netlink_ext_ack *extack);
#endif /* __ZL3073X_FLASH_H */

419
drivers/dpll/zl3073x/fw.c Normal file
View File

@ -0,0 +1,419 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/array_size.h>
#include <linux/build_bug.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/netlink.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "core.h"
#include "flash.h"
#include "fw.h"
#define ZL3073X_FW_ERR_PFX "FW load failed: "
#define ZL3073X_FW_ERR_MSG(_extack, _msg, ...) \
NL_SET_ERR_MSG_FMT_MOD((_extack), ZL3073X_FW_ERR_PFX _msg, \
## __VA_ARGS__)
enum zl3073x_flash_type {
ZL3073X_FLASH_TYPE_NONE = 0,
ZL3073X_FLASH_TYPE_SECTORS,
ZL3073X_FLASH_TYPE_PAGE,
ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
};
struct zl3073x_fw_component_info {
const char *name;
size_t max_size;
enum zl3073x_flash_type flash_type;
u32 load_addr;
u32 dest_page;
u32 copy_page;
};
static const struct zl3073x_fw_component_info component_info[] = {
[ZL_FW_COMPONENT_UTIL] = {
.name = "utility",
.max_size = 0x4000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_NONE,
},
[ZL_FW_COMPONENT_FW1] = {
.name = "firmware1",
.max_size = 0x35000,
.load_addr = 0x20002000,
.flash_type = ZL3073X_FLASH_TYPE_SECTORS,
.dest_page = 0x020,
},
[ZL_FW_COMPONENT_FW2] = {
.name = "firmware2",
.max_size = 0x0040,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
.dest_page = 0x3e0,
.copy_page = 0x000,
},
[ZL_FW_COMPONENT_FW3] = {
.name = "firmware3",
.max_size = 0x0248,
.load_addr = 0x20000400,
.flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
.dest_page = 0x3e4,
.copy_page = 0x004,
},
[ZL_FW_COMPONENT_CFG0] = {
.name = "config0",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x3d0,
},
[ZL_FW_COMPONENT_CFG1] = {
.name = "config1",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x3c0,
},
[ZL_FW_COMPONENT_CFG2] = {
.name = "config2",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x3b0,
},
[ZL_FW_COMPONENT_CFG3] = {
.name = "config3",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x3a0,
},
[ZL_FW_COMPONENT_CFG4] = {
.name = "config4",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x390,
},
[ZL_FW_COMPONENT_CFG5] = {
.name = "config5",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x380,
},
[ZL_FW_COMPONENT_CFG6] = {
.name = "config6",
.max_size = 0x1000,
.load_addr = 0x20000000,
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
.dest_page = 0x370,
},
};
/* Sanity check */
static_assert(ARRAY_SIZE(component_info) == ZL_FW_NUM_COMPONENTS);
/**
* zl3073x_fw_component_alloc - Alloc structure to hold firmware component
* @size: size of buffer to store data
*
* Return: pointer to allocated component structure or NULL on error.
*/
static struct zl3073x_fw_component *
zl3073x_fw_component_alloc(size_t size)
{
struct zl3073x_fw_component *comp;
comp = kzalloc(sizeof(*comp), GFP_KERNEL);
if (!comp)
return NULL;
comp->size = size;
comp->data = kzalloc(size, GFP_KERNEL);
if (!comp->data) {
kfree(comp);
return NULL;
}
return comp;
}
/**
* zl3073x_fw_component_free - Free allocated component structure
* @comp: pointer to allocated component
*/
static void
zl3073x_fw_component_free(struct zl3073x_fw_component *comp)
{
if (comp)
kfree(comp->data);
kfree(comp);
}
/**
* zl3073x_fw_component_id_get - Get ID for firmware component name
* @name: input firmware component name
*
* Return:
* - ZL3073X_FW_COMPONENT_* ID for known component name
* - ZL3073X_FW_COMPONENT_INVALID if the given name is unknown
*/
static enum zl3073x_fw_component_id
zl3073x_fw_component_id_get(const char *name)
{
enum zl3073x_fw_component_id id;
for (id = 0; id < ZL_FW_NUM_COMPONENTS; id++)
if (!strcasecmp(name, component_info[id].name))
return id;
return ZL_FW_COMPONENT_INVALID;
}
/**
* zl3073x_fw_component_load - Load component from firmware source
* @zldev: zl3073x device structure
* @pcomp: pointer to loaded component
* @psrc: data pointer to load component from
* @psize: remaining bytes in buffer
* @extack: netlink extack pointer to report errors
*
* The function allocates single firmware component and loads the data from
* the buffer specified by @psrc and @psize. Pointer to allocated component
* is stored in output @pcomp. Source data pointer @psrc and remaining bytes
* @psize are updated accordingly.
*
* Return:
* * 1 when component was allocated and loaded
* * 0 when there is no component to load
* * <0 on error
*/
static ssize_t
zl3073x_fw_component_load(struct zl3073x_dev *zldev,
struct zl3073x_fw_component **pcomp,
const char **psrc, size_t *psize,
struct netlink_ext_ack *extack)
{
const struct zl3073x_fw_component_info *info;
struct zl3073x_fw_component *comp = NULL;
struct device *dev = zldev->dev;
enum zl3073x_fw_component_id id;
char buf[32], name[16];
u32 count, size, *dest;
int pos, rc;
/* Fetch image name and size from input */
strscpy(buf, *psrc, min(sizeof(buf), *psize));
rc = sscanf(buf, "%15s %u %n", name, &count, &pos);
if (!rc) {
/* No more data */
return 0;
} else if (rc == 1 || count > U32_MAX / sizeof(u32)) {
ZL3073X_FW_ERR_MSG(extack, "invalid component size");
return -EINVAL;
}
*psrc += pos;
*psize -= pos;
dev_dbg(dev, "Firmware component '%s' found\n", name);
id = zl3073x_fw_component_id_get(name);
if (id == ZL_FW_COMPONENT_INVALID) {
ZL3073X_FW_ERR_MSG(extack, "unknown component type '%s'", name);
return -EINVAL;
}
info = &component_info[id];
size = count * sizeof(u32); /* get size in bytes */
/* Check image size validity */
if (size > component_info[id].max_size) {
ZL3073X_FW_ERR_MSG(extack,
"[%s] component is too big (%u bytes)\n",
info->name, size);
return -EINVAL;
}
dev_dbg(dev, "Indicated component image size: %u bytes\n", size);
/* Alloc component */
comp = zl3073x_fw_component_alloc(size);
if (!comp) {
ZL3073X_FW_ERR_MSG(extack, "failed to alloc memory");
return -ENOMEM;
}
comp->id = id;
/* Load component data from firmware source */
for (dest = comp->data; count; count--, dest++) {
strscpy(buf, *psrc, min(sizeof(buf), *psize));
rc = sscanf(buf, "%x %n", dest, &pos);
if (!rc)
goto err_data;
*psrc += pos;
*psize -= pos;
}
*pcomp = comp;
return 1;
err_data:
ZL3073X_FW_ERR_MSG(extack, "[%s] invalid or missing data", info->name);
zl3073x_fw_component_free(comp);
return -ENODATA;
}
/**
* zl3073x_fw_free - Free allocated firmware
* @fw: firmware pointer
*
* The function frees existing firmware allocated by @zl3073x_fw_load.
*/
void zl3073x_fw_free(struct zl3073x_fw *fw)
{
size_t i;
if (!fw)
return;
for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++)
zl3073x_fw_component_free(fw->component[i]);
kfree(fw);
}
/**
* zl3073x_fw_load - Load all components from source
* @zldev: zl3073x device structure
* @data: source buffer pointer
* @size: size of source buffer
* @extack: netlink extack pointer to report errors
*
* The functions allocate firmware structure and loads all components from
* the given buffer specified by @data and @size.
*
* Return: pointer to firmware on success, error pointer on error
*/
struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
size_t size, struct netlink_ext_ack *extack)
{
struct zl3073x_fw_component *comp;
enum zl3073x_fw_component_id id;
struct zl3073x_fw *fw;
ssize_t rc;
/* Allocate firmware structure */
fw = kzalloc(sizeof(*fw), GFP_KERNEL);
if (!fw)
return ERR_PTR(-ENOMEM);
do {
/* Load single component */
rc = zl3073x_fw_component_load(zldev, &comp, &data, &size,
extack);
if (rc <= 0)
/* Everything was read or error occurred */
break;
id = comp->id;
/* Report error if the given component is present twice
* or more.
*/
if (fw->component[id]) {
ZL3073X_FW_ERR_MSG(extack,
"duplicate component '%s' detected",
component_info[id].name);
zl3073x_fw_component_free(comp);
rc = -EINVAL;
break;
}
fw->component[id] = comp;
} while (true);
if (rc) {
/* Free allocated firmware in case of error */
zl3073x_fw_free(fw);
return ERR_PTR(rc);
}
return fw;
}
/**
* zl3073x_fw_component_flash - Flash all components
* @zldev: zl3073x device structure
* @comp: pointer to components array
* @extack: netlink extack pointer to report errors
*
* Return: 0 in case of success or negative number otherwise.
*/
static int
zl3073x_fw_component_flash(struct zl3073x_dev *zldev,
struct zl3073x_fw_component *comp,
struct netlink_ext_ack *extack)
{
const struct zl3073x_fw_component_info *info;
int rc;
info = &component_info[comp->id];
switch (info->flash_type) {
case ZL3073X_FLASH_TYPE_NONE:
/* Non-flashable component - used for utility */
return 0;
case ZL3073X_FLASH_TYPE_SECTORS:
rc = zl3073x_flash_sectors(zldev, info->name, info->dest_page,
info->load_addr, comp->data,
comp->size, extack);
break;
case ZL3073X_FLASH_TYPE_PAGE:
rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
info->load_addr, comp->data, comp->size,
extack);
break;
case ZL3073X_FLASH_TYPE_PAGE_AND_COPY:
rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
info->load_addr, comp->data, comp->size,
extack);
if (!rc)
rc = zl3073x_flash_page_copy(zldev, info->name,
info->dest_page,
info->copy_page, extack);
break;
}
if (rc)
ZL3073X_FW_ERR_MSG(extack, "Failed to flash component '%s'",
info->name);
return rc;
}
int zl3073x_fw_flash(struct zl3073x_dev *zldev, struct zl3073x_fw *zlfw,
struct netlink_ext_ack *extack)
{
int i, rc = 0;
for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++) {
if (!zlfw->component[i])
continue; /* Component is not present */
rc = zl3073x_fw_component_flash(zldev, zlfw->component[i],
extack);
if (rc)
break;
}
return rc;
}

52
drivers/dpll/zl3073x/fw.h Normal file
View File

@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_FW_H
#define _ZL3073X_FW_H
/*
* enum zl3073x_fw_component_id - Identifiers for possible flash components
*/
enum zl3073x_fw_component_id {
ZL_FW_COMPONENT_INVALID = -1,
ZL_FW_COMPONENT_UTIL = 0,
ZL_FW_COMPONENT_FW1,
ZL_FW_COMPONENT_FW2,
ZL_FW_COMPONENT_FW3,
ZL_FW_COMPONENT_CFG0,
ZL_FW_COMPONENT_CFG1,
ZL_FW_COMPONENT_CFG2,
ZL_FW_COMPONENT_CFG3,
ZL_FW_COMPONENT_CFG4,
ZL_FW_COMPONENT_CFG5,
ZL_FW_COMPONENT_CFG6,
ZL_FW_NUM_COMPONENTS
};
/**
* struct zl3073x_fw_component - Firmware component
* @id: Flash component ID
* @size: Size of the buffer
* @data: Pointer to buffer with component data
*/
struct zl3073x_fw_component {
enum zl3073x_fw_component_id id;
size_t size;
void *data;
};
/**
* struct zl3073x_fw - Firmware bundle
* @component: firmware components array
*/
struct zl3073x_fw {
struct zl3073x_fw_component *component[ZL_FW_NUM_COMPONENTS];
};
struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
size_t size, struct netlink_ext_ack *extack);
void zl3073x_fw_free(struct zl3073x_fw *fw);
int zl3073x_fw_flash(struct zl3073x_dev *zldev, struct zl3073x_fw *zlfw,
struct netlink_ext_ack *extack);
#endif /* _ZL3073X_FW_H */

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "core.h"
static int zl3073x_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct zl3073x_dev *zldev;
zldev = zl3073x_devm_alloc(dev);
if (IS_ERR(zldev))
return PTR_ERR(zldev);
zldev->regmap = devm_regmap_init_i2c(client, &zl3073x_regmap_config);
if (IS_ERR(zldev->regmap))
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
"Failed to initialize regmap\n");
return zl3073x_dev_probe(zldev, i2c_get_match_data(client));
}
static const struct i2c_device_id zl3073x_i2c_id[] = {
{
.name = "zl30731",
.driver_data = (kernel_ulong_t)&zl30731_chip_info,
},
{
.name = "zl30732",
.driver_data = (kernel_ulong_t)&zl30732_chip_info,
},
{
.name = "zl30733",
.driver_data = (kernel_ulong_t)&zl30733_chip_info,
},
{
.name = "zl30734",
.driver_data = (kernel_ulong_t)&zl30734_chip_info,
},
{
.name = "zl30735",
.driver_data = (kernel_ulong_t)&zl30735_chip_info,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id);
static const struct of_device_id zl3073x_i2c_of_match[] = {
{ .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
{ .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
{ .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
{ .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
{ .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match);
static struct i2c_driver zl3073x_i2c_driver = {
.driver = {
.name = "zl3073x-i2c",
.of_match_table = zl3073x_i2c_of_match,
},
.probe_new = zl3073x_i2c_probe,
.id_table = zl3073x_i2c_id,
};
module_i2c_driver(zl3073x_i2c_driver);
MODULE_AUTHOR("Ivan Vecera <ivecera@redhat.com>");
MODULE_DESCRIPTION("Microchip ZL3073x I2C driver");
MODULE_IMPORT_NS(ZL3073X);
MODULE_LICENSE("GPL");

358
drivers/dpll/zl3073x/prop.c Normal file
View File

@ -0,0 +1,358 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/array_size.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fwnode.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "core.h"
#include "prop.h"
/**
* zl3073x_pin_check_freq - verify frequency for given pin
* @zldev: pointer to zl3073x device
* @dir: pin direction
* @id: pin index
* @freq: frequency to check
*
* The function checks the given frequency is valid for the device. For input
* pins it checks that the frequency can be factorized using supported base
* frequencies. For output pins it checks that the frequency divides connected
* synth frequency without remainder.
*
* Return: true if the frequency is valid, false if not.
*/
static bool
zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,
u8 id, u64 freq)
{
if (freq > U32_MAX)
goto err_inv_freq;
if (dir == DPLL_PIN_DIRECTION_INPUT) {
int rc;
/* Check if the frequency can be factorized */
rc = zl3073x_ref_freq_factorize(freq, NULL, NULL);
if (rc)
goto err_inv_freq;
} else {
u32 synth_freq;
u8 out, synth;
/* Get output pin synthesizer */
out = zl3073x_output_pin_out_get(id);
synth = zl3073x_out_synth_get(zldev, out);
/* Get synth frequency */
synth_freq = zl3073x_synth_freq_get(zldev, synth);
/* Check the frequency divides synth frequency */
if (synth_freq % (u32)freq)
goto err_inv_freq;
}
return true;
err_inv_freq:
dev_warn(zldev->dev,
"Unsupported frequency %llu Hz in firmware node\n", freq);
return false;
}
/**
* zl3073x_prop_pin_package_label_set - get package label for the pin
* @zldev: pointer to zl3073x device
* @props: pointer to pin properties
* @dir: pin direction
* @id: pin index
*
* Generates package label string and stores it into pin properties structure.
*
* Possible formats:
* REF<n> - differential input reference
* REF<n>P & REF<n>N - single-ended input reference (P or N pin)
* OUT<n> - differential output
* OUT<n>P & OUT<n>N - single-ended output (P or N pin)
*/
static void
zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev,
struct zl3073x_pin_props *props,
enum dpll_pin_direction dir, u8 id)
{
const char *prefix, *suffix;
bool is_diff;
if (dir == DPLL_PIN_DIRECTION_INPUT) {
u8 ref;
prefix = "REF";
ref = zl3073x_input_pin_ref_get(id);
is_diff = zl3073x_ref_is_diff(zldev, ref);
} else {
u8 out;
prefix = "OUT";
out = zl3073x_output_pin_out_get(id);
is_diff = zl3073x_out_is_diff(zldev, out);
}
if (!is_diff)
suffix = zl3073x_is_p_pin(id) ? "P" : "N";
else
suffix = ""; /* No suffix for differential one */
snprintf(props->package_label, sizeof(props->package_label), "%s%u%s",
prefix, id / 2, suffix);
/* Set package_label pointer in DPLL core properties to generated
* string.
*/
props->dpll_props.package_label = props->package_label;
}
/**
* zl3073x_prop_pin_fwnode_get - get fwnode for given pin
* @zldev: pointer to zl3073x device
* @props: pointer to pin properties
* @dir: pin direction
* @id: pin index
*
* Return: 0 on success, -ENOENT if the firmware node does not exist
*/
static int
zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev,
struct zl3073x_pin_props *props,
enum dpll_pin_direction dir, u8 id)
{
struct fwnode_handle *pins_node, *pin_node;
const char *node_name;
if (dir == DPLL_PIN_DIRECTION_INPUT)
node_name = "input-pins";
else
node_name = "output-pins";
/* Get node containing input or output pins */
pins_node = device_get_named_child_node(zldev->dev, node_name);
if (!pins_node) {
dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name);
return -ENOENT;
}
/* Enumerate child pin nodes and find the requested one */
fwnode_for_each_child_node(pins_node, pin_node) {
u32 reg;
if (fwnode_property_read_u32(pin_node, "reg", &reg))
continue;
if (id == reg)
break;
}
/* Release pin parent node */
fwnode_handle_put(pins_node);
/* Save found node */
props->fwnode = pin_node;
dev_dbg(zldev->dev, "Firmware node for %s %sfound\n",
props->package_label, pin_node ? "" : "NOT ");
return pin_node ? 0 : -ENOENT;
}
/**
* zl3073x_pin_props_get - get pin properties
* @zldev: pointer to zl3073x device
* @dir: pin direction
* @index: pin index
*
* The function looks for firmware node for the given pin if it is provided
* by the system firmware (DT or ACPI), allocates pin properties structure,
* generates package label string according pin type and optionally fetches
* board label, connection type, supported frequencies and esync capability
* from the firmware node if it does exist.
*
* Pointer that is returned by this function should be freed using
* @zl3073x_pin_props_put().
*
* Return:
* * pointer to allocated pin properties structure on success
* * error pointer in case of error
*/
struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
enum dpll_pin_direction dir,
u8 index)
{
struct dpll_pin_frequency *ranges;
struct zl3073x_pin_props *props;
int i, j, num_freqs, rc;
const char *type;
u64 *freqs;
props = kzalloc(sizeof(*props), GFP_KERNEL);
if (!props)
return ERR_PTR(-ENOMEM);
/* Set default pin type and capabilities */
if (dir == DPLL_PIN_DIRECTION_INPUT) {
props->dpll_props.type = DPLL_PIN_TYPE_EXT;
props->dpll_props.capabilities =
DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
} else {
props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
}
props->dpll_props.phase_range.min = S32_MIN;
props->dpll_props.phase_range.max = S32_MAX;
zl3073x_prop_pin_package_label_set(zldev, props, dir, index);
/* Get firmware node for the given pin */
rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index);
if (rc)
return props; /* Return if it does not exist */
/* Look for label property and store the value as board label */
fwnode_property_read_string(props->fwnode, "label",
&props->dpll_props.board_label);
/* Look for pin type property and translate its value to DPLL
* pin type enum if it is present.
*/
if (!fwnode_property_read_string(props->fwnode, "connection-type",
&type)) {
if (!strcmp(type, "ext"))
props->dpll_props.type = DPLL_PIN_TYPE_EXT;
else if (!strcmp(type, "gnss"))
props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
else if (!strcmp(type, "int"))
props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
else if (!strcmp(type, "synce"))
props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
else
dev_warn(zldev->dev,
"Unknown or unsupported pin type '%s'\n",
type);
}
/* Check if the pin supports embedded sync control */
props->esync_control = fwnode_property_read_bool(props->fwnode,
"esync-control");
/* Read supported frequencies property if it is specified */
num_freqs = fwnode_property_count_u64(props->fwnode,
"supported-frequencies-hz");
if (num_freqs <= 0)
/* Return if the property does not exist or number is 0 */
return props;
/* The firmware node specifies list of supported frequencies while
* DPLL core pin properties requires list of frequency ranges.
* So read the frequency list into temporary array.
*/
freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
if (!freqs) {
rc = -ENOMEM;
goto err_alloc_freqs;
}
/* Read frequencies list from firmware node */
fwnode_property_read_u64_array(props->fwnode,
"supported-frequencies-hz", freqs,
num_freqs);
/* Allocate frequency ranges list and fill it */
ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL);
if (!ranges) {
rc = -ENOMEM;
goto err_alloc_ranges;
}
/* Convert list of frequencies to list of frequency ranges but
* filter-out frequencies that are not representable by device
*/
for (i = 0, j = 0; i < num_freqs; i++) {
struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) {
ranges[j] = freq;
j++;
}
}
/* Save number of freq ranges and pointer to them into pin properties */
props->dpll_props.freq_supported = ranges;
props->dpll_props.freq_supported_num = j;
/* Free temporary array */
kfree(freqs);
return props;
err_alloc_ranges:
kfree(freqs);
err_alloc_freqs:
fwnode_handle_put(props->fwnode);
kfree(props);
return ERR_PTR(rc);
}
/**
* zl3073x_pin_props_put - release pin properties
* @props: pin properties to free
*
* The function deallocates given pin properties structure.
*/
void zl3073x_pin_props_put(struct zl3073x_pin_props *props)
{
/* Free supported frequency ranges list if it is present */
kfree(props->dpll_props.freq_supported);
/* Put firmware handle if it is present */
if (props->fwnode)
fwnode_handle_put(props->fwnode);
kfree(props);
}
/**
* zl3073x_prop_dpll_type_get - get DPLL channel type
* @zldev: pointer to zl3073x device
* @index: DPLL channel index
*
* Return: DPLL type for given DPLL channel
*/
enum dpll_type
zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index)
{
const char *types[ZL3073X_MAX_CHANNELS];
int count;
/* Read dpll types property from firmware */
count = device_property_read_string_array(zldev->dev, "dpll-types",
types, ARRAY_SIZE(types));
/* Return default if property or entry for given channel is missing */
if (index >= count)
return DPLL_TYPE_PPS;
if (!strcmp(types[index], "pps"))
return DPLL_TYPE_PPS;
else if (!strcmp(types[index], "eec"))
return DPLL_TYPE_EEC;
dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n",
types[index]);
return DPLL_TYPE_PPS; /* Default */
}

View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_PROP_H
#define _ZL3073X_PROP_H
#include <linux/dpll.h>
#include "core.h"
struct fwnode_handle;
/**
* struct zl3073x_pin_props - pin properties
* @fwnode: pin firmware node
* @dpll_props: DPLL core pin properties
* @package_label: pin package label
* @esync_control: embedded sync support
*/
struct zl3073x_pin_props {
struct fwnode_handle *fwnode;
struct dpll_pin_properties dpll_props;
char package_label[8];
bool esync_control;
};
enum dpll_type zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index);
struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
enum dpll_pin_direction dir,
u8 index);
void zl3073x_pin_props_put(struct zl3073x_pin_props *props);
#endif /* _ZL3073X_PROP_H */

317
drivers/dpll/zl3073x/regs.h Normal file
View File

@ -0,0 +1,317 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_REGS_H
#define _ZL3073X_REGS_H
#include <linux/bitfield.h>
#include <linux/bits.h>
/*
* Register address structure:
* ===========================
* 25 19 18 16 15 7 6 0
* +------------------------------------------+
* | max_offset | size | page | page_offset |
* +------------------------------------------+
*
* page_offset ... <0x00..0x7F>
* page .......... HW page number
* size .......... register byte size (1, 2, 4 or 6)
* max_offset .... maximal offset for indexed registers
* (for non-indexed regs max_offset == page_offset)
*/
#define ZL_REG_OFFSET_MASK GENMASK(6, 0)
#define ZL_REG_PAGE_MASK GENMASK(15, 7)
#define ZL_REG_SIZE_MASK GENMASK(18, 16)
#define ZL_REG_MAX_OFFSET_MASK GENMASK(25, 19)
#define ZL_REG_ADDR_MASK GENMASK(15, 0)
#define ZL_REG_OFFSET(_reg) FIELD_GET(ZL_REG_OFFSET_MASK, _reg)
#define ZL_REG_PAGE(_reg) FIELD_GET(ZL_REG_PAGE_MASK, _reg)
#define ZL_REG_MAX_OFFSET(_reg) FIELD_GET(ZL_REG_MAX_OFFSET_MASK, _reg)
#define ZL_REG_SIZE(_reg) FIELD_GET(ZL_REG_SIZE_MASK, _reg)
#define ZL_REG_ADDR(_reg) FIELD_GET(ZL_REG_ADDR_MASK, _reg)
/**
* ZL_REG_IDX - define indexed register
* @_idx: index of register to access
* @_page: register page
* @_offset: register offset in page
* @_size: register byte size (1, 2, 4 or 6)
* @_items: number of register indices
* @_stride: stride between items in bytes
*
* All parameters except @_idx should be constant.
*/
#define ZL_REG_IDX(_idx, _page, _offset, _size, _items, _stride) \
(FIELD_PREP(ZL_REG_OFFSET_MASK, \
(_offset) + (_idx) * (_stride)) | \
FIELD_PREP_CONST(ZL_REG_PAGE_MASK, _page) | \
FIELD_PREP_CONST(ZL_REG_SIZE_MASK, _size) | \
FIELD_PREP_CONST(ZL_REG_MAX_OFFSET_MASK, \
(_offset) + ((_items) - 1) * (_stride)))
/**
* ZL_REG - define simple (non-indexed) register
* @_page: register page
* @_offset: register offset in page
* @_size: register byte size (1, 2, 4 or 6)
*
* All parameters should be constant.
*/
#define ZL_REG(_page, _offset, _size) \
ZL_REG_IDX(0, _page, _offset, _size, 1, 0)
/**************************
* Register Page 0, General
**************************/
#define ZL_REG_INFO ZL_REG(0, 0x00, 1)
#define ZL_INFO_READY BIT(7)
#define ZL_REG_ID ZL_REG(0, 0x01, 2)
#define ZL_REG_REVISION ZL_REG(0, 0x03, 2)
#define ZL_REG_FW_VER ZL_REG(0, 0x05, 2)
#define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4)
#define ZL_REG_RESET_STATUS ZL_REG(0, 0x18, 1)
#define ZL_REG_RESET_STATUS_RESET BIT(0)
/*************************
* Register Page 2, Status
*************************/
#define ZL_REG_REF_MON_STATUS(_idx) \
ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1)
#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */
#define ZL_REG_DPLL_MON_STATUS(_idx) \
ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1)
#define ZL_DPLL_MON_STATUS_STATE GENMASK(1, 0)
#define ZL_DPLL_MON_STATUS_STATE_ACQUIRING 0
#define ZL_DPLL_MON_STATUS_STATE_LOCK 1
#define ZL_DPLL_MON_STATUS_STATE_HOLDOVER 2
#define ZL_DPLL_MON_STATUS_HO_READY BIT(2)
#define ZL_REG_DPLL_REFSEL_STATUS(_idx) \
ZL_REG_IDX(_idx, 2, 0x30, 1, ZL3073X_MAX_CHANNELS, 1)
#define ZL_DPLL_REFSEL_STATUS_REFSEL GENMASK(3, 0)
#define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4)
#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4
#define ZL_REG_REF_FREQ(_idx) \
ZL_REG_IDX(_idx, 2, 0x44, 4, ZL3073X_NUM_REFS, 4)
/**********************
* Register Page 4, Ref
**********************/
#define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1)
#define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0)
#define ZL_REG_REF_FREQ_MEAS_CTRL ZL_REG(4, 0x1c, 1)
#define ZL_REF_FREQ_MEAS_CTRL GENMASK(1, 0)
#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ 1
#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF 2
#define ZL_REF_FREQ_MEAS_CTRL_DPLL_FREQ_OFF 3
#define ZL_REG_REF_FREQ_MEAS_MASK_3_0 ZL_REG(4, 0x1d, 1)
#define ZL_REF_FREQ_MEAS_MASK_3_0(_ref) BIT(_ref)
#define ZL_REG_REF_FREQ_MEAS_MASK_4 ZL_REG(4, 0x1e, 1)
#define ZL_REF_FREQ_MEAS_MASK_4(_ref) BIT((_ref) - 8)
#define ZL_REG_DPLL_MEAS_REF_FREQ_CTRL ZL_REG(4, 0x1f, 1)
#define ZL_DPLL_MEAS_REF_FREQ_CTRL_EN BIT(0)
#define ZL_DPLL_MEAS_REF_FREQ_CTRL_IDX GENMASK(6, 4)
#define ZL_REG_REF_PHASE(_idx) \
ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6)
/***********************
* Register Page 5, DPLL
***********************/
#define ZL_REG_DPLL_MODE_REFSEL(_idx) \
ZL_REG_IDX(_idx, 5, 0x04, 1, ZL3073X_MAX_CHANNELS, 4)
#define ZL_DPLL_MODE_REFSEL_MODE GENMASK(2, 0)
#define ZL_DPLL_MODE_REFSEL_MODE_FREERUN 0
#define ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER 1
#define ZL_DPLL_MODE_REFSEL_MODE_REFLOCK 2
#define ZL_DPLL_MODE_REFSEL_MODE_AUTO 3
#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4
#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4)
#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1)
#define ZL_DPLL_MEAS_CTRL_EN BIT(0)
#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4)
#define ZL_REG_DPLL_MEAS_IDX ZL_REG(5, 0x51, 1)
#define ZL_DPLL_MEAS_IDX GENMASK(2, 0)
#define ZL_REG_DPLL_PHASE_ERR_READ_MASK ZL_REG(5, 0x54, 1)
#define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \
ZL_REG_IDX(_idx, 5, 0x55, 6, ZL3073X_MAX_CHANNELS, 6)
/***********************************
* Register Page 9, Synth and Output
***********************************/
#define ZL_REG_SYNTH_CTRL(_idx) \
ZL_REG_IDX(_idx, 9, 0x00, 1, ZL3073X_NUM_SYNTHS, 1)
#define ZL_SYNTH_CTRL_EN BIT(0)
#define ZL_SYNTH_CTRL_DPLL_SEL GENMASK(6, 4)
#define ZL_REG_SYNTH_PHASE_SHIFT_CTRL ZL_REG(9, 0x1e, 1)
#define ZL_REG_SYNTH_PHASE_SHIFT_MASK ZL_REG(9, 0x1f, 1)
#define ZL_REG_SYNTH_PHASE_SHIFT_INTVL ZL_REG(9, 0x20, 1)
#define ZL_REG_SYNTH_PHASE_SHIFT_DATA ZL_REG(9, 0x21, 2)
#define ZL_REG_OUTPUT_CTRL(_idx) \
ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1)
#define ZL_OUTPUT_CTRL_EN BIT(0)
#define ZL_OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4)
/*******************************
* Register Page 10, Ref Mailbox
*******************************/
#define ZL_REG_REF_MB_MASK ZL_REG(10, 0x02, 2)
#define ZL_REG_REF_MB_SEM ZL_REG(10, 0x04, 1)
#define ZL_REF_MB_SEM_WR BIT(0)
#define ZL_REF_MB_SEM_RD BIT(1)
#define ZL_REG_REF_FREQ_BASE ZL_REG(10, 0x05, 2)
#define ZL_REG_REF_FREQ_MULT ZL_REG(10, 0x07, 2)
#define ZL_REG_REF_RATIO_M ZL_REG(10, 0x09, 2)
#define ZL_REG_REF_RATIO_N ZL_REG(10, 0x0b, 2)
#define ZL_REG_REF_CONFIG ZL_REG(10, 0x0d, 1)
#define ZL_REF_CONFIG_ENABLE BIT(0)
#define ZL_REF_CONFIG_DIFF_EN BIT(2)
#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6)
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
#define ZL_REF_ESYNC_DIV_1HZ 0
/********************************
* Register Page 12, DPLL Mailbox
********************************/
#define ZL_REG_DPLL_MB_MASK ZL_REG(12, 0x02, 2)
#define ZL_REG_DPLL_MB_SEM ZL_REG(12, 0x04, 1)
#define ZL_DPLL_MB_SEM_WR BIT(0)
#define ZL_DPLL_MB_SEM_RD BIT(1)
#define ZL_REG_DPLL_REF_PRIO(_idx) \
ZL_REG_IDX(_idx, 12, 0x52, 1, ZL3073X_NUM_REFS / 2, 1)
#define ZL_DPLL_REF_PRIO_REF_P GENMASK(3, 0)
#define ZL_DPLL_REF_PRIO_REF_N GENMASK(7, 4)
#define ZL_DPLL_REF_PRIO_MAX 14
#define ZL_DPLL_REF_PRIO_NONE 15
/*********************************
* Register Page 13, Synth Mailbox
*********************************/
#define ZL_REG_SYNTH_MB_MASK ZL_REG(13, 0x02, 2)
#define ZL_REG_SYNTH_MB_SEM ZL_REG(13, 0x04, 1)
#define ZL_SYNTH_MB_SEM_WR BIT(0)
#define ZL_SYNTH_MB_SEM_RD BIT(1)
#define ZL_REG_SYNTH_FREQ_BASE ZL_REG(13, 0x06, 2)
#define ZL_REG_SYNTH_FREQ_MULT ZL_REG(13, 0x08, 4)
#define ZL_REG_SYNTH_FREQ_M ZL_REG(13, 0x0c, 2)
#define ZL_REG_SYNTH_FREQ_N ZL_REG(13, 0x0e, 2)
/**********************************
* Register Page 14, Output Mailbox
**********************************/
#define ZL_REG_OUTPUT_MB_MASK ZL_REG(14, 0x02, 2)
#define ZL_REG_OUTPUT_MB_SEM ZL_REG(14, 0x04, 1)
#define ZL_OUTPUT_MB_SEM_WR BIT(0)
#define ZL_OUTPUT_MB_SEM_RD BIT(1)
#define ZL_REG_OUTPUT_MODE ZL_REG(14, 0x05, 1)
#define ZL_OUTPUT_MODE_CLOCK_TYPE GENMASK(2, 0)
#define ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL 0
#define ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC 1
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT GENMASK(7, 4)
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED 0
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS 1
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF 2
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM 3
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2 4
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P 5
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N 6
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_INV 7
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV 12
#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV 15
#define ZL_REG_OUTPUT_DIV ZL_REG(14, 0x0c, 4)
#define ZL_REG_OUTPUT_WIDTH ZL_REG(14, 0x10, 4)
#define ZL_REG_OUTPUT_ESYNC_PERIOD ZL_REG(14, 0x14, 4)
#define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4)
#define ZL_REG_OUTPUT_PHASE_COMP ZL_REG(14, 0x20, 4)
/*
* Register Page 255 - HW registers access
*/
#define ZL_REG_HWREG_OP ZL_REG(0xff, 0x00, 1)
#define ZL_HWREG_OP_WRITE 0x28
#define ZL_HWREG_OP_READ 0x29
#define ZL_HWREG_OP_PENDING BIT(1)
#define ZL_REG_HWREG_ADDR ZL_REG(0xff, 0x04, 4)
#define ZL_REG_HWREG_WRITE_DATA ZL_REG(0xff, 0x08, 4)
#define ZL_REG_HWREG_READ_DATA ZL_REG(0xff, 0x0c, 4)
/*
* Registers available in flash mode
*/
#define ZL_REG_FLASH_HASH ZL_REG(0, 0x78, 4)
#define ZL_REG_FLASH_FAMILY ZL_REG(0, 0x7c, 1)
#define ZL_REG_FLASH_RELEASE ZL_REG(0, 0x7d, 1)
#define ZL_REG_HOST_CONTROL ZL_REG(1, 0x02, 1)
#define ZL_HOST_CONTROL_ENABLE BIT(0)
#define ZL_REG_IMAGE_START_ADDR ZL_REG(1, 0x04, 4)
#define ZL_REG_IMAGE_SIZE ZL_REG(1, 0x08, 4)
#define ZL_REG_FLASH_INDEX_READ ZL_REG(1, 0x0c, 4)
#define ZL_REG_FLASH_INDEX_WRITE ZL_REG(1, 0x10, 4)
#define ZL_REG_FILL_PATTERN ZL_REG(1, 0x14, 4)
#define ZL_REG_WRITE_FLASH ZL_REG(1, 0x18, 1)
#define ZL_WRITE_FLASH_OP GENMASK(2, 0)
#define ZL_WRITE_FLASH_OP_DONE 0x0
#define ZL_WRITE_FLASH_OP_SECTORS 0x2
#define ZL_WRITE_FLASH_OP_PAGE 0x3
#define ZL_WRITE_FLASH_OP_COPY_PAGE 0x4
#define ZL_REG_FLASH_INFO ZL_REG(2, 0x00, 1)
#define ZL_FLASH_INFO_SECTOR_SIZE GENMASK(3, 0)
#define ZL_FLASH_INFO_SECTOR_4K 0
#define ZL_FLASH_INFO_SECTOR_64K 1
#define ZL_REG_ERROR_COUNT ZL_REG(2, 0x04, 4)
#define ZL_REG_ERROR_CAUSE ZL_REG(2, 0x08, 4)
#define ZL_REG_OP_STATE ZL_REG(2, 0x14, 1)
#define ZL_OP_STATE_NO_COMMAND 0
#define ZL_OP_STATE_PENDING 1
#define ZL_OP_STATE_DONE 2
#endif /* _ZL3073X_REGS_H */

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "core.h"
static int zl3073x_spi_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct zl3073x_dev *zldev;
zldev = zl3073x_devm_alloc(dev);
if (IS_ERR(zldev))
return PTR_ERR(zldev);
zldev->regmap = devm_regmap_init_spi(spi, &zl3073x_regmap_config);
if (IS_ERR(zldev->regmap))
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
"Failed to initialize regmap\n");
return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi));
}
static const struct spi_device_id zl3073x_spi_id[] = {
{
.name = "zl30731",
.driver_data = (kernel_ulong_t)&zl30731_chip_info
},
{
.name = "zl30732",
.driver_data = (kernel_ulong_t)&zl30732_chip_info,
},
{
.name = "zl30733",
.driver_data = (kernel_ulong_t)&zl30733_chip_info,
},
{
.name = "zl30734",
.driver_data = (kernel_ulong_t)&zl30734_chip_info,
},
{
.name = "zl30735",
.driver_data = (kernel_ulong_t)&zl30735_chip_info,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, zl3073x_spi_id);
static const struct of_device_id zl3073x_spi_of_match[] = {
{ .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
{ .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
{ .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
{ .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
{ .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match);
static struct spi_driver zl3073x_spi_driver = {
.driver = {
.name = "zl3073x-spi",
.of_match_table = zl3073x_spi_of_match,
},
.probe = zl3073x_spi_probe,
.id_table = zl3073x_spi_id,
};
module_spi_driver(zl3073x_spi_driver);
MODULE_AUTHOR("Ivan Vecera <ivecera@redhat.com>");
MODULE_DESCRIPTION("Microchip ZL3073x SPI driver");
MODULE_IMPORT_NS(ZL3073X);
MODULE_LICENSE("GPL");

View File

@ -1124,11 +1124,24 @@ static int octep_set_features(struct net_device *dev, netdev_features_t features
return err;
}
static bool octep_is_vf_valid(struct octep_device *oct, int vf)
{
if (vf >= CFG_GET_ACTIVE_VFS(oct->conf)) {
netdev_err(oct->netdev, "Invalid VF ID %d\n", vf);
return false;
}
return true;
}
static int octep_get_vf_config(struct net_device *dev, int vf,
struct ifla_vf_info *ivi)
{
struct octep_device *oct = netdev_priv(dev);
if (!octep_is_vf_valid(oct, vf))
return -EINVAL;
ivi->vf = vf;
ether_addr_copy(ivi->mac, oct->vf_info[vf].mac_addr);
ivi->spoofchk = true;
@ -1143,6 +1156,9 @@ static int octep_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
struct octep_device *oct = netdev_priv(dev);
int err;
if (!octep_is_vf_valid(oct, vf))
return -EINVAL;
if (!is_valid_ether_addr(mac)) {
dev_err(&oct->pdev->dev, "Invalid MAC Address %pM\n", mac);
return -EADDRNOTAVAIL;

View File

@ -658,7 +658,7 @@ static void del_sw_hw_rule(struct fs_node *node)
BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) |
BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS);
fte->act_dests.action.action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
mlx5_fc_local_destroy(rule->dest_attr.counter);
mlx5_fc_local_put(rule->dest_attr.counter);
goto out;
}

View File

@ -345,6 +345,7 @@ struct mlx5_fc {
/* last{packets,bytes} are used for calculating deltas since last reading. */
u64 lastpackets;
u64 lastbytes;
RH_KABI_EXTEND(refcount_t fc_local_refcount)
};
struct mlx5_fc_bulk {

View File

@ -562,17 +562,36 @@ mlx5_fc_local_create(u32 counter_id, u32 offset, u32 bulk_size)
counter->id = counter_id;
fc_bulk->base_id = counter_id - offset;
fc_bulk->fs_bulk.bulk_len = bulk_size;
refcount_set(&fc_bulk->hws_data.hws_action_refcount, 0);
mutex_init(&fc_bulk->hws_data.lock);
counter->bulk = fc_bulk;
refcount_set(&counter->fc_local_refcount, 1);
return counter;
}
EXPORT_SYMBOL(mlx5_fc_local_create);
void mlx5_fc_local_destroy(struct mlx5_fc *counter)
{
if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
return;
kfree(counter->bulk);
kfree(counter);
}
EXPORT_SYMBOL(mlx5_fc_local_destroy);
void mlx5_fc_local_get(struct mlx5_fc *counter)
{
if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
return;
refcount_inc(&counter->fc_local_refcount);
}
void mlx5_fc_local_put(struct mlx5_fc *counter)
{
if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
return;
if (!refcount_dec_and_test(&counter->fc_local_refcount))
return;
mlx5_fc_local_destroy(counter);
}

View File

@ -407,15 +407,21 @@ struct mlx5hws_action *mlx5_fc_get_hws_action(struct mlx5hws_context *ctx,
{
struct mlx5_fs_hws_create_action_ctx create_ctx;
struct mlx5_fc_bulk *fc_bulk = counter->bulk;
struct mlx5hws_action *hws_action;
create_ctx.hws_ctx = ctx;
create_ctx.id = fc_bulk->base_id;
create_ctx.actions_type = MLX5HWS_ACTION_TYP_CTR;
return mlx5_fs_get_hws_action(&fc_bulk->hws_data, &create_ctx);
mlx5_fc_local_get(counter);
hws_action = mlx5_fs_get_hws_action(&fc_bulk->hws_data, &create_ctx);
if (!hws_action)
mlx5_fc_local_put(counter);
return hws_action;
}
void mlx5_fc_put_hws_action(struct mlx5_fc *counter)
{
mlx5_fs_put_hws_action(&counter->bulk->hws_data);
mlx5_fc_local_put(counter);
}

View File

@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev)
}
EXPORT_SYMBOL_GPL(spi_get_device_id);
const void *spi_get_device_match_data(const struct spi_device *sdev)
{
const void *match;
match = device_get_match_data(&sdev->dev);
if (match)
return match;
return (const void *)spi_get_device_id(sdev)->driver_data;
}
EXPORT_SYMBOL_GPL(spi_get_device_match_data);
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);

0
include/config/ZL3073X Normal file
View File

View File

View File

View File

@ -27,6 +27,7 @@ CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
CONFIG_RTLBTCOEXIST=m
CONFIG_NFT_NAT=m
CONFIG_POLYNOMIAL=m
CONFIG_ZL3073X_SPI=m
CONFIG_ZRAM_WRITEBACK=y
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
CONFIG_INPUT_KEYBOARD=y
@ -925,6 +926,7 @@ CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
CONFIG_CB710_DEBUG_ASSUMPTIONS=y
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_SBITMAP=y
CONFIG_ZL3073X=m
CONFIG_IMX_INTMUX=y
CONFIG_MSPRO_BLOCK=m
CONFIG_GPIO_XLP=m
@ -2949,6 +2951,7 @@ CONFIG_IPV6_OPTIMISTIC_DAD=y
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
CONFIG_NETFS_SUPPORT=m
CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
CONFIG_ZL3073X_I2C=m
CONFIG_MD_AUTODETECT=y
CONFIG_PANIC_ON_OOPS_VALUE=1
CONFIG_PLDMFW=y

View File

@ -146,7 +146,6 @@ deps_config := \
fs/ext4/Kconfig \
fs/ext2/Kconfig \
fs/Kconfig \
drivers/dpll/Kconfig \
drivers/hte/Kconfig \
drivers/most/Kconfig \
drivers/counter/Kconfig \
@ -934,6 +933,8 @@ deps_config := \
drivers/pinctrl/aspeed/Kconfig \
drivers/pinctrl/actions/Kconfig \
drivers/pinctrl/Kconfig \
drivers/dpll/zl3073x/Kconfig \
drivers/dpll/Kconfig \
drivers/ptp/Kconfig \
drivers/pps/generators/Kconfig \
drivers/pps/clients/Kconfig \

View File

@ -29,6 +29,7 @@
#define CONFIG_RTLBTCOEXIST_MODULE 1
#define CONFIG_NFT_NAT_MODULE 1
#define CONFIG_POLYNOMIAL_MODULE 1
#define CONFIG_ZL3073X_SPI_MODULE 1
#define CONFIG_ZRAM_WRITEBACK 1
#define CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK 1
#define CONFIG_INPUT_KEYBOARD 1
@ -927,6 +928,7 @@
#define CONFIG_CB710_DEBUG_ASSUMPTIONS 1
#define CONFIG_SPLIT_PTLOCK_CPUS 4
#define CONFIG_SBITMAP 1
#define CONFIG_ZL3073X_MODULE 1
#define CONFIG_IMX_INTMUX 1
#define CONFIG_MSPRO_BLOCK_MODULE 1
#define CONFIG_GPIO_XLP_MODULE 1
@ -2951,6 +2953,7 @@
#define CONFIG_ARCH_HAS_ELF_RANDOMIZE 1
#define CONFIG_NETFS_SUPPORT_MODULE 1
#define CONFIG_HAVE_FUNCTION_ARG_ACCESS_API 1
#define CONFIG_ZL3073X_I2C_MODULE 1
#define CONFIG_MD_AUTODETECT 1
#define CONFIG_PANIC_ON_OOPS_VALUE 1
#define CONFIG_PLDMFW 1

View File

@ -308,6 +308,8 @@ struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging);
void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter);
struct mlx5_fc *mlx5_fc_local_create(u32 counter_id, u32 offset, u32 bulk_size);
void mlx5_fc_local_destroy(struct mlx5_fc *counter);
void mlx5_fc_local_get(struct mlx5_fc *counter);
void mlx5_fc_local_put(struct mlx5_fc *counter);
u64 mlx5_fc_query_lastuse(struct mlx5_fc *counter);
void mlx5_fc_query_cached(struct mlx5_fc *counter,
u64 *bytes, u64 *packets, u64 *lastuse);

View File

@ -1590,6 +1590,9 @@ extern void spi_unregister_device(struct spi_device *spi);
extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev);
extern const void *
spi_get_device_match_data(const struct spi_device *sdev);
static inline bool
spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer)
{

View File

@ -212,6 +212,7 @@ void j1939_priv_get(struct j1939_priv *priv);
/* notify/alert all j1939 sockets bound to ifindex */
void j1939_sk_netdev_event_netdown(struct j1939_priv *priv);
void j1939_sk_netdev_event_unregister(struct j1939_priv *priv);
int j1939_cancel_active_session(struct j1939_priv *priv, struct sock *sk);
void j1939_tp_init(struct j1939_priv *priv);

View File

@ -381,6 +381,11 @@ static int j1939_netdev_notify(struct notifier_block *nb,
j1939_sk_netdev_event_netdown(priv);
j1939_ecu_unmap_all(priv);
break;
case NETDEV_UNREGISTER:
j1939_cancel_active_session(priv, NULL);
j1939_sk_netdev_event_netdown(priv);
j1939_sk_netdev_event_unregister(priv);
break;
}
j1939_priv_put(priv);

View File

@ -1296,6 +1296,55 @@ void j1939_sk_netdev_event_netdown(struct j1939_priv *priv)
read_unlock_bh(&priv->j1939_socks_lock);
}
void j1939_sk_netdev_event_unregister(struct j1939_priv *priv)
{
struct sock *sk;
struct j1939_sock *jsk;
bool wait_rcu = false;
rescan: /* The caller is holding a ref on this "priv" via j1939_priv_get_by_ndev(). */
read_lock_bh(&priv->j1939_socks_lock);
list_for_each_entry(jsk, &priv->j1939_socks, list) {
/* Skip if j1939_jsk_add() is not called on this socket. */
if (!(jsk->state & J1939_SOCK_BOUND))
continue;
sk = &jsk->sk;
sock_hold(sk);
read_unlock_bh(&priv->j1939_socks_lock);
/* Check if j1939_jsk_del() is not yet called on this socket after holding
* socket's lock, for both j1939_sk_bind() and j1939_sk_release() call
* j1939_jsk_del() with socket's lock held.
*/
lock_sock(sk);
if (jsk->state & J1939_SOCK_BOUND) {
/* Neither j1939_sk_bind() nor j1939_sk_release() called j1939_jsk_del().
* Make this socket no longer bound, by pretending as if j1939_sk_bind()
* dropped old references but did not get new references.
*/
j1939_jsk_del(priv, jsk);
j1939_local_ecu_put(priv, jsk->addr.src_name, jsk->addr.sa);
j1939_netdev_stop(priv);
/* Call j1939_priv_put() now and prevent j1939_sk_sock_destruct() from
* calling the corresponding j1939_priv_put().
*
* j1939_sk_sock_destruct() is supposed to call j1939_priv_put() after
* an RCU grace period. But since the caller is holding a ref on this
* "priv", we can defer synchronize_rcu() until immediately before
* the caller calls j1939_priv_put().
*/
j1939_priv_put(priv);
jsk->priv = NULL;
wait_rcu = true;
}
release_sock(sk);
sock_put(sk);
goto rescan;
}
read_unlock_bh(&priv->j1939_socks_lock);
if (wait_rcu)
synchronize_rcu();
}
static int j1939_sk_no_ioctlcmd(struct socket *sock, unsigned int cmd,
unsigned long arg)
{

View File

@ -0,0 +1 @@
CONFIG_ZL3073X=m

View File

@ -0,0 +1 @@
CONFIG_ZL3073X_I2C=m

View File

@ -0,0 +1 @@
CONFIG_ZL3073X_SPI=m

View File

@ -0,0 +1 @@
# CONFIG_ZL3073X is not set

View File

@ -0,0 +1 @@
# CONFIG_ZL3073X_I2C is not set

View File

@ -0,0 +1 @@
# CONFIG_ZL3073X_SPI is not set

View File

@ -0,0 +1 @@
CONFIG_I2C_MUX_PCA954x=m

View File

@ -1,3 +1,45 @@
* Sat Nov 29 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.13.1.el9_7]
- can: j1939: add missing calls in NETDEV_UNREGISTER notification handler (CKI Backport Bot) [RHEL-124105] {CVE-2025-39925}
- can: j1939: implement NETDEV_UNREGISTER notification handler (CKI Backport Bot) [RHEL-124105] {CVE-2025-39925}
Resolves: RHEL-124105
* Thu Nov 27 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.12.1.el9_7]
- x86/hyperv: Fix kdump on Azure CVMs (Li Tian) [RHEL-129776]
- net/mlx5: fs, fix UAF in flow counter release (Michal Schmidt) [RHEL-124428] {CVE-2025-39979}
- octeon_ep: Validate the VF ID (Kamal Heib) [RHEL-117604]
- dpll: zl3073x: fix kernel-doc name and missing parameter in fw.c (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Fix output pin registration (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Increase maximum size of flash utility (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Fix double free in zl3073x_devlink_flash_update() (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Implement devlink flash callback (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Add firmware loading functionality (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Add low-level flash functions (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Add functions to access hardware registers (Ivan Vecera) [RHEL-116162]
- dpll: zl3073x: Handle missing or corrupted flash configuration (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Refactor DPLL initialization (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: ZL3073X_I2C and ZL3073X_SPI should depend on NET (Ivan Vecera) [RHEL-113083]
- dpll: Make ZL3073X invisible (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Fix build failure (Ivan Vecera) [RHEL-113083]
- redhat/configs: enable CONFIG_ZL3073X* (Ivan Vecera) [RHEL-113083]
- redhat/configs: enable CONFIG_I2C_MUX_PCA954x on x86 (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to get fractional frequency offset (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to adjust phase (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Implement phase offset monitor feature (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to get phase offset on connected input pin (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to get/set esync on pins (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to get/set frequency on pins (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Implement input pin state setting in automatic mode (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Add support to get/set priority on input pins (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Implement input pin selection in manual mode (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Register DPLL devices and pins (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Read DPLL types and pin properties from system firmware (Ivan Vecera) [RHEL-113083]
- dpll: zl3073x: Fetch invariants during probe (Ivan Vecera) [RHEL-113083]
- dpll: Add basic Microchip ZL3073x support (Ivan Vecera) [RHEL-113083]
- spi: Introduce spi_get_device_match_data() helper (Ivan Vecera) [RHEL-113083]
- dt-bindings: dpll: Add support for Microchip Azurite chip family (Ivan Vecera) [RHEL-113083]
- dt-bindings: dpll: Add DPLL device and pin (Ivan Vecera) [RHEL-113083]
Resolves: RHEL-113083, RHEL-116162, RHEL-117604, RHEL-124428, RHEL-129776
* Tue Nov 25 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.11.1.el9_7]
- tcp: Don't call reqsk_fastopen_remove() in tcp_conn_request(). (Antoine Tenart) [RHEL-120668]
- tcp: Clear tcp_sk(sk)->fastopen_rsk in tcp_disconnect(). (Antoine Tenart) [RHEL-120668] {CVE-2025-39955}