Add full CPI (Converged PHY Interface) command handling required for E825C devices. The CPI interface allows the driver to interact with PHY-side control logic through the LM/PHY command registers, including enabling/disabling/selection of PHY reference clock. This patch introduces: - a new CPI subsystem (ice_cpi.c / ice_cpi.h) implementing the CPI request/acknowledge state machine, including REQ/ACK protocol, command execution, and response handling - helper functions for reading/writing PHY registers over Sideband Queue - CPI command execution API (ice_cpi_exec) and a helper for enabling or disabling Tx reference clocks (CPI 0xF1 opcode 'Config PHY clocking') - assurance of CPI transaction serialization into the CPI core. CPI REQ/ACK is a multi-step handshake and must be executed atomically per PHY. Centralize the lock in ice_cpi_exec() and use adapter-scoped per-PHY mutexes, which match the hardware sharing model across PFs. - addition of the non-posted write opcode (wr_np) to SBQ - Makefile integration to build CPI support together with the PTP stack This provides the infrastructure necessary to support PHY-side configuration flows on E825C and is required for advanced link control and Tx reference clock management. Reviewed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com> Link: https://patch.msgid.link/20260607183045.1213735-12-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
152 lines
4.1 KiB
C
152 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright Red Hat
|
|
|
|
#include <linux/cleanup.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/xarray.h>
|
|
#include "ice_adapter.h"
|
|
#include "ice.h"
|
|
|
|
static DEFINE_XARRAY(ice_adapters);
|
|
static DEFINE_MUTEX(ice_adapters_mutex);
|
|
|
|
#define ICE_ADAPTER_FIXED_INDEX BIT_ULL(63)
|
|
|
|
#define ICE_ADAPTER_INDEX_E825C \
|
|
(ICE_DEV_ID_E825C_BACKPLANE | ICE_ADAPTER_FIXED_INDEX)
|
|
|
|
static u64 ice_adapter_index(struct pci_dev *pdev)
|
|
{
|
|
switch (pdev->device) {
|
|
case ICE_DEV_ID_E825C_BACKPLANE:
|
|
case ICE_DEV_ID_E825C_QSFP:
|
|
case ICE_DEV_ID_E825C_SFP:
|
|
case ICE_DEV_ID_E825C_SGMII:
|
|
/* E825C devices have multiple NACs which are connected to the
|
|
* same clock source, and which must share the same
|
|
* ice_adapter structure. We can't use the serial number since
|
|
* each NAC has its own NVM generated with its own unique
|
|
* Device Serial Number. Instead, rely on the embedded nature
|
|
* of the E825C devices, and use a fixed index. This relies on
|
|
* the fact that all E825C physical functions in a given
|
|
* system are part of the same overall device.
|
|
*/
|
|
return ICE_ADAPTER_INDEX_E825C;
|
|
default:
|
|
return pci_get_dsn(pdev) & ~ICE_ADAPTER_FIXED_INDEX;
|
|
}
|
|
}
|
|
|
|
static unsigned long ice_adapter_xa_index(struct pci_dev *pdev)
|
|
{
|
|
u64 index = ice_adapter_index(pdev);
|
|
|
|
#if BITS_PER_LONG == 64
|
|
return index;
|
|
#else
|
|
return (u32)index ^ (u32)(index >> 32);
|
|
#endif
|
|
}
|
|
|
|
static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev)
|
|
{
|
|
struct ice_adapter *adapter;
|
|
|
|
adapter = kzalloc_obj(*adapter);
|
|
if (!adapter)
|
|
return NULL;
|
|
|
|
adapter->index = ice_adapter_index(pdev);
|
|
spin_lock_init(&adapter->ptp_gltsyn_time_lock);
|
|
spin_lock_init(&adapter->txq_ctx_lock);
|
|
for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++)
|
|
mutex_init(&adapter->cpi_phy_lock[i]);
|
|
refcount_set(&adapter->refcount, 1);
|
|
|
|
mutex_init(&adapter->ports.lock);
|
|
INIT_LIST_HEAD(&adapter->ports.ports);
|
|
|
|
return adapter;
|
|
}
|
|
|
|
static void ice_adapter_free(struct ice_adapter *adapter)
|
|
{
|
|
WARN_ON(!list_empty(&adapter->ports.ports));
|
|
for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++)
|
|
mutex_destroy(&adapter->cpi_phy_lock[i]);
|
|
mutex_destroy(&adapter->ports.lock);
|
|
|
|
kfree(adapter);
|
|
}
|
|
|
|
/**
|
|
* ice_adapter_get - Get a shared ice_adapter structure.
|
|
* @pdev: Pointer to the pci_dev whose driver is getting the ice_adapter.
|
|
*
|
|
* Gets a pointer to a shared ice_adapter structure. Physical functions (PFs)
|
|
* of the same multi-function PCI device share one ice_adapter structure.
|
|
* The ice_adapter is reference-counted. The PF driver must use ice_adapter_put
|
|
* to release its reference.
|
|
*
|
|
* Context: Process, may sleep.
|
|
* Return: Pointer to ice_adapter on success.
|
|
* ERR_PTR() on error. -ENOMEM is the only possible error.
|
|
*/
|
|
struct ice_adapter *ice_adapter_get(struct pci_dev *pdev)
|
|
{
|
|
struct ice_adapter *adapter;
|
|
unsigned long index;
|
|
int err;
|
|
|
|
index = ice_adapter_xa_index(pdev);
|
|
scoped_guard(mutex, &ice_adapters_mutex) {
|
|
adapter = xa_load(&ice_adapters, index);
|
|
if (adapter) {
|
|
refcount_inc(&adapter->refcount);
|
|
WARN_ON_ONCE(adapter->index != ice_adapter_index(pdev));
|
|
return adapter;
|
|
}
|
|
err = xa_reserve(&ice_adapters, index, GFP_KERNEL);
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
adapter = ice_adapter_new(pdev);
|
|
if (!adapter) {
|
|
xa_release(&ice_adapters, index);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
xa_store(&ice_adapters, index, adapter, GFP_KERNEL);
|
|
}
|
|
return adapter;
|
|
}
|
|
|
|
/**
|
|
* ice_adapter_put - Release a reference to the shared ice_adapter structure.
|
|
* @pdev: Pointer to the pci_dev whose driver is releasing the ice_adapter.
|
|
*
|
|
* Releases the reference to ice_adapter previously obtained with
|
|
* ice_adapter_get.
|
|
*
|
|
* Context: Process, may sleep.
|
|
*/
|
|
void ice_adapter_put(struct pci_dev *pdev)
|
|
{
|
|
struct ice_adapter *adapter;
|
|
unsigned long index;
|
|
|
|
index = ice_adapter_xa_index(pdev);
|
|
scoped_guard(mutex, &ice_adapters_mutex) {
|
|
adapter = xa_load(&ice_adapters, index);
|
|
if (WARN_ON(!adapter))
|
|
return;
|
|
if (!refcount_dec_and_test(&adapter->refcount))
|
|
return;
|
|
|
|
WARN_ON(xa_erase(&ice_adapters, index) != adapter);
|
|
}
|
|
ice_adapter_free(adapter);
|
|
}
|