Import of kernel-5.14.0-687.15.1.el9_8
This commit is contained in:
parent
a19517f55f
commit
9bb58011ba
@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
|
||||
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
Frequency monitor
|
||||
=================
|
||||
|
||||
Some DPLL devices may offer the capability to measure the actual
|
||||
frequency of all available input pins. The attribute and current feature state
|
||||
shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
|
||||
command for supported DPLL devices. In such cases, users can also control
|
||||
the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
|
||||
``enum dpll_feature_state`` values for the attribute.
|
||||
Once enabled the measured input frequency for each input pin shall be
|
||||
returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value
|
||||
is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER``
|
||||
as the divider.
|
||||
|
||||
=============================== ========================
|
||||
``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
Embedded SYNC
|
||||
=============
|
||||
|
||||
@ -411,6 +429,8 @@ according to attribute purpose.
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
pin
|
||||
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
||||
``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of
|
||||
an input pin in mHz
|
||||
==================================== ==================================
|
||||
|
||||
==================================== =================================
|
||||
|
||||
@ -240,6 +240,20 @@ definitions:
|
||||
integer part of a measured phase offset value.
|
||||
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
|
||||
fractional part of a measured phase offset value.
|
||||
-
|
||||
type: const
|
||||
name: pin-measured-frequency-divider
|
||||
value: 1000
|
||||
doc: |
|
||||
pin measured frequency divider allows userspace to calculate
|
||||
a value of measured input frequency as a fractional value with
|
||||
three digit decimal precision (millihertz).
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of
|
||||
a measured frequency value.
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of
|
||||
a measured frequency value.
|
||||
-
|
||||
type: enum
|
||||
name: feature-state
|
||||
@ -319,6 +333,13 @@ attribute-sets:
|
||||
name: phase-offset-avg-factor
|
||||
type: u32
|
||||
doc: Averaging factor applied to calculation of reported phase offset.
|
||||
-
|
||||
name: frequency-monitor
|
||||
type: u32
|
||||
enum: feature-state
|
||||
doc: Current or desired state of the frequency monitor feature.
|
||||
If enabled, dpll device shall measure all currently available
|
||||
inputs for their actual input frequency.
|
||||
-
|
||||
name: pin
|
||||
enum-name: dpll_a_pin
|
||||
@ -456,6 +477,17 @@ attribute-sets:
|
||||
Value is in PPT (parts per trillion, 10^-12).
|
||||
Note: This attribute provides higher resolution than the standard
|
||||
fractional-frequency-offset (which is in PPM).
|
||||
-
|
||||
name: measured-frequency
|
||||
type: u64
|
||||
doc: |
|
||||
The measured frequency of the input pin in millihertz (mHz).
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz)
|
||||
of a measured frequency value.
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
|
||||
of a measured frequency value.
|
||||
|
||||
-
|
||||
name: pin-parent-device
|
||||
@ -544,6 +576,7 @@ operations:
|
||||
- type
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
- frequency-monitor
|
||||
|
||||
dump:
|
||||
reply: *dev-attrs
|
||||
@ -563,6 +596,7 @@ operations:
|
||||
- mode
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
- frequency-monitor
|
||||
-
|
||||
name: device-create-ntf
|
||||
doc: Notification about device appearing
|
||||
@ -643,6 +677,7 @@ operations:
|
||||
- esync-frequency-supported
|
||||
- esync-pulse
|
||||
- reference-sync
|
||||
- measured-frequency
|
||||
|
||||
dump:
|
||||
request:
|
||||
|
||||
@ -86,6 +86,7 @@ struct kvm_task_sleep_node {
|
||||
struct swait_queue_head wq;
|
||||
u32 token;
|
||||
int cpu;
|
||||
bool dummy;
|
||||
};
|
||||
|
||||
static struct kvm_task_sleep_head {
|
||||
@ -117,15 +118,26 @@ static bool kvm_async_pf_queue_task(u32 token, struct kvm_task_sleep_node *n)
|
||||
raw_spin_lock(&b->lock);
|
||||
e = _find_apf_task(b, token);
|
||||
if (e) {
|
||||
/* dummy entry exist -> wake up was delivered ahead of PF */
|
||||
hlist_del(&e->link);
|
||||
struct kvm_task_sleep_node *dummy = NULL;
|
||||
|
||||
/*
|
||||
* The entry can either be a 'dummy' entry (which is put on the
|
||||
* list when wake-up happens ahead of APF handling completion)
|
||||
* or a token from another task which should not be touched.
|
||||
*/
|
||||
if (e->dummy) {
|
||||
hlist_del(&e->link);
|
||||
dummy = e;
|
||||
}
|
||||
|
||||
raw_spin_unlock(&b->lock);
|
||||
kfree(e);
|
||||
kfree(dummy);
|
||||
return false;
|
||||
}
|
||||
|
||||
n->token = token;
|
||||
n->cpu = smp_processor_id();
|
||||
n->dummy = false;
|
||||
init_swait_queue_head(&n->wq);
|
||||
hlist_add_head(&n->link, &b->list);
|
||||
raw_spin_unlock(&b->lock);
|
||||
@ -228,6 +240,7 @@ again:
|
||||
}
|
||||
dummy->token = token;
|
||||
dummy->cpu = smp_processor_id();
|
||||
dummy->dummy = true;
|
||||
init_swait_queue_head(&dummy->wq);
|
||||
hlist_add_head(&dummy->link, &b->list);
|
||||
dummy = NULL;
|
||||
|
||||
@ -39,7 +39,7 @@ struct authenc_request_ctx {
|
||||
|
||||
static void authenc_request_complete(struct aead_request *req, int err)
|
||||
{
|
||||
if (err != -EINPROGRESS)
|
||||
if (err != -EINPROGRESS && err != -EBUSY)
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
@ -109,27 +109,42 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void authenc_geniv_ahash_done(struct crypto_async_request *areq, int err)
|
||||
static void authenc_geniv_ahash_finish(struct aead_request *req)
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
struct authenc_instance_ctx *ictx = aead_instance_ctx(inst);
|
||||
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
|
||||
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
scatterwalk_map_and_copy(ahreq->result, req->dst,
|
||||
req->assoclen + req->cryptlen,
|
||||
crypto_aead_authsize(authenc), 1);
|
||||
}
|
||||
|
||||
out:
|
||||
static void authenc_geniv_ahash_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
|
||||
if (!err)
|
||||
authenc_geniv_ahash_finish(req);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
/*
|
||||
* Used when the ahash request was invoked in the async callback context
|
||||
* of the previous skcipher request. Eat any EINPROGRESS notifications.
|
||||
*/
|
||||
static void authenc_geniv_ahash_done2(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
|
||||
if (!err)
|
||||
authenc_geniv_ahash_finish(req);
|
||||
authenc_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int crypto_authenc_genicv(struct aead_request *req, unsigned int mask)
|
||||
{
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
@ -138,6 +153,7 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
struct crypto_ahash *auth = ctx->auth;
|
||||
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
|
||||
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
|
||||
unsigned int flags = aead_request_flags(req) & ~mask;
|
||||
u8 *hash = areq_ctx->tail;
|
||||
int err;
|
||||
|
||||
@ -148,7 +164,8 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
ahash_request_set_crypt(ahreq, req->dst, hash,
|
||||
req->assoclen + req->cryptlen);
|
||||
ahash_request_set_callback(ahreq, flags,
|
||||
authenc_geniv_ahash_done, req);
|
||||
mask ? authenc_geniv_ahash_done2 :
|
||||
authenc_geniv_ahash_done, req);
|
||||
|
||||
err = crypto_ahash_digest(ahreq);
|
||||
if (err)
|
||||
@ -165,12 +182,11 @@ static void crypto_authenc_encrypt_done(struct crypto_async_request *req,
|
||||
{
|
||||
struct aead_request *areq = req->data;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_authenc_genicv(areq, 0);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
aead_request_complete(areq, err);
|
||||
return;
|
||||
}
|
||||
err = crypto_authenc_genicv(areq, CRYPTO_TFM_REQ_MAY_SLEEP);
|
||||
authenc_request_complete(areq, err);
|
||||
}
|
||||
|
||||
@ -223,11 +239,18 @@ static int crypto_authenc_encrypt(struct aead_request *req)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return crypto_authenc_genicv(req, aead_request_flags(req));
|
||||
return crypto_authenc_genicv(req, 0);
|
||||
}
|
||||
|
||||
static void authenc_decrypt_tail_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
|
||||
authenc_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
unsigned int flags)
|
||||
unsigned int mask)
|
||||
{
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
@ -238,6 +261,7 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
|
||||
ictx->reqoff);
|
||||
unsigned int authsize = crypto_aead_authsize(authenc);
|
||||
unsigned int flags = aead_request_flags(req) & ~mask;
|
||||
u8 *ihash = ahreq->result + authsize;
|
||||
struct scatterlist *src, *dst;
|
||||
|
||||
@ -253,8 +277,10 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
|
||||
|
||||
skcipher_request_set_tfm(skreq, ctx->enc);
|
||||
skcipher_request_set_callback(skreq, aead_request_flags(req),
|
||||
req->base.complete, req->base.data);
|
||||
skcipher_request_set_callback(skreq, flags,
|
||||
mask ? authenc_decrypt_tail_done :
|
||||
req->base.complete,
|
||||
mask ? req : req->base.data);
|
||||
skcipher_request_set_crypt(skreq, src, dst,
|
||||
req->cryptlen - authsize, req->iv);
|
||||
|
||||
@ -266,12 +292,11 @@ static void authenc_verify_ahash_done(struct crypto_async_request *areq,
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_authenc_decrypt_tail(req, 0);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
aead_request_complete(req, err);
|
||||
return;
|
||||
}
|
||||
err = crypto_authenc_decrypt_tail(req, CRYPTO_TFM_REQ_MAY_SLEEP);
|
||||
authenc_request_complete(req, err);
|
||||
}
|
||||
|
||||
@ -301,7 +326,7 @@ static int crypto_authenc_decrypt(struct aead_request *req)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return crypto_authenc_decrypt_tail(req, aead_request_flags(req));
|
||||
return crypto_authenc_decrypt_tail(req, 0);
|
||||
}
|
||||
|
||||
static int crypto_authenc_init_tfm(struct crypto_aead *tfm)
|
||||
|
||||
@ -880,7 +880,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
|
||||
if (WARN_ON(!ops) ||
|
||||
WARN_ON(!ops->state_on_dpll_get) ||
|
||||
WARN_ON(!ops->direction_get))
|
||||
WARN_ON(!ops->direction_get) ||
|
||||
WARN_ON(ops->measured_freq_get &&
|
||||
(!dpll_device_ops(dpll)->freq_monitor_get ||
|
||||
!dpll_device_ops(dpll)->freq_monitor_set)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
|
||||
@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
enum dpll_feature_state state;
|
||||
int ret;
|
||||
|
||||
if (ops->freq_monitor_set && ops->freq_monitor_get) {
|
||||
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
&state, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
|
||||
struct dpll_device *dpll,
|
||||
@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
ffo);
|
||||
}
|
||||
|
||||
static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
|
||||
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
||||
struct dpll_device *dpll = ref->dpll;
|
||||
enum dpll_feature_state state;
|
||||
u64 measured_freq;
|
||||
int ret;
|
||||
|
||||
if (!ops->measured_freq_get)
|
||||
return 0;
|
||||
ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
&state, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (state == DPLL_FEATURE_STATE_DISABLE)
|
||||
return 0;
|
||||
ret = ops->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
dpll, dpll_priv(dpll), &measured_freq,
|
||||
extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
sizeof(measured_freq), &measured_freq,
|
||||
DPLL_A_PIN_PAD))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
|
||||
@ -670,6 +722,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_ffo(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
|
||||
@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -958,6 +1016,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
extack);
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
enum dpll_feature_state state = nla_get_u32(a), old_state;
|
||||
int ret;
|
||||
|
||||
if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
|
||||
NL_SET_ERR_MSG_ATTR(extack, a,
|
||||
"dpll device not capable of frequency monitor");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
|
||||
extack);
|
||||
if (ret) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"unable to get current state of frequency monitor");
|
||||
return ret;
|
||||
}
|
||||
if (state == old_state)
|
||||
return 0;
|
||||
|
||||
return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
struct netlink_ext_ack *extack)
|
||||
@ -1888,6 +1972,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case DPLL_A_FREQUENCY_MONITOR:
|
||||
ret = dpll_freq_monitor_set(dpll, a,
|
||||
info->extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,11 +42,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_SET - do */
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
|
||||
[DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_ID_GET - do */
|
||||
@ -114,7 +115,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
|
||||
.doit = dpll_nl_device_set_doit,
|
||||
.post_doit = dpll_post_doit,
|
||||
.policy = dpll_device_set_nl_policy,
|
||||
.maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
.maxattr = DPLL_A_FREQUENCY_MONITOR,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_ZL3073X) += zl3073x.o
|
||||
zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
|
||||
out.o prop.o ref.o synth.o
|
||||
zl3073x-objs := chan.o core.o devlink.o dpll.o \
|
||||
flash.o fw.o out.o prop.o ref.o synth.o
|
||||
|
||||
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
|
||||
zl3073x_i2c-objs := i2c.o
|
||||
|
||||
165
drivers/dpll/zl3073x/chan.c
Normal file
165
drivers/dpll/zl3073x/chan.c
Normal file
@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "chan.h"
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* zl3073x_chan_state_update - update DPLL channel status from HW
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: DPLL channel index
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
int rc;
|
||||
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
|
||||
&chan->mon_status);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
|
||||
&chan->refsel_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: DPLL channel index to fetch state for
|
||||
*
|
||||
* Reads the mode_refsel register and reference priority registers for
|
||||
* the given DPLL channel and stores the raw values for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
int rc, i;
|
||||
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
&chan->mode_refsel);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
|
||||
zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));
|
||||
|
||||
rc = zl3073x_chan_state_update(zldev, index);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_dbg(zldev->dev,
|
||||
"DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
|
||||
index, zl3073x_chan_lock_state_get(chan),
|
||||
zl3073x_chan_is_ho_ready(chan) ? 1 : 0,
|
||||
zl3073x_chan_refsel_state_get(chan),
|
||||
zl3073x_chan_refsel_ref_get(chan));
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read DPLL configuration from mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read reference priority registers */
|
||||
for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i),
|
||||
&chan->ref_prio[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_state_get - get current DPLL channel state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: DPLL channel index to get state for
|
||||
*
|
||||
* Return: pointer to given DPLL channel state
|
||||
*/
|
||||
const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index)
|
||||
{
|
||||
return &zldev->chan[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_state_set - commit DPLL channel state changes to hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: DPLL channel index to set state for
|
||||
* @chan: desired channel state
|
||||
*
|
||||
* Skips the HW write if the configuration is unchanged, and otherwise
|
||||
* writes only the changed registers to hardware. The mode_refsel register
|
||||
* is written directly, while the reference priority registers are written
|
||||
* via the DPLL mailbox interface.
|
||||
*
|
||||
* Return: 0 on success, <0 on HW error
|
||||
*/
|
||||
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_chan *chan)
|
||||
{
|
||||
struct zl3073x_chan *dchan = &zldev->chan[index];
|
||||
int rc, i;
|
||||
|
||||
/* Skip HW write if configuration hasn't changed */
|
||||
if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
|
||||
return 0;
|
||||
|
||||
/* Direct register write for mode_refsel */
|
||||
if (dchan->mode_refsel != chan->mode_refsel) {
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
chan->mode_refsel);
|
||||
if (rc)
|
||||
return rc;
|
||||
dchan->mode_refsel = chan->mode_refsel;
|
||||
}
|
||||
|
||||
/* Mailbox write for ref_prio if changed */
|
||||
if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) {
|
||||
dchan->cfg = chan->cfg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read DPLL configuration into mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Update changed ref_prio registers */
|
||||
for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
||||
if (dchan->ref_prio[i] != chan->ref_prio[i]) {
|
||||
rc = zl3073x_write_u8(zldev,
|
||||
ZL_REG_DPLL_REF_PRIO(i),
|
||||
chan->ref_prio[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit DPLL configuration */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
|
||||
ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* After successful write store new state */
|
||||
dchan->cfg = chan->cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
174
drivers/dpll/zl3073x/chan.h
Normal file
174
drivers/dpll/zl3073x/chan.h
Normal file
@ -0,0 +1,174 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _ZL3073X_CHAN_H
|
||||
#define _ZL3073X_CHAN_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
|
||||
struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_chan - DPLL channel state
|
||||
* @mode_refsel: mode and reference selection register value
|
||||
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
|
||||
* @mon_status: monitor status register value
|
||||
* @refsel_status: reference selection status register value
|
||||
*/
|
||||
struct zl3073x_chan {
|
||||
struct_group(cfg,
|
||||
u8 mode_refsel;
|
||||
u8 ref_prio[ZL3073X_NUM_REFS / 2];
|
||||
);
|
||||
struct_group(stat,
|
||||
u8 mon_status;
|
||||
u8 refsel_status;
|
||||
);
|
||||
};
|
||||
|
||||
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index);
|
||||
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_chan *chan);
|
||||
|
||||
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
|
||||
/**
|
||||
* zl3073x_chan_mode_get - get DPLL channel operating mode
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: reference selection mode of the given DPLL channel
|
||||
*/
|
||||
static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, chan->mode_refsel);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_ref_get - get manually selected reference
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: reference selected in forced reference lock mode
|
||||
*/
|
||||
static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, chan->mode_refsel);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_mode_set - set DPLL channel operating mode
|
||||
* @chan: pointer to channel state
|
||||
* @mode: mode to set
|
||||
*/
|
||||
static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
||||
{
|
||||
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_MODE, &chan->mode_refsel, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_ref_set - set manually selected reference
|
||||
* @chan: pointer to channel state
|
||||
* @ref: reference to set
|
||||
*/
|
||||
static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
||||
{
|
||||
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_REF, &chan->mode_refsel, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_ref_prio_get - get reference priority
|
||||
* @chan: pointer to channel state
|
||||
* @ref: input reference index
|
||||
*
|
||||
* Return: priority of the given reference <0, 15>
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)
|
||||
{
|
||||
u8 val = chan->ref_prio[ref / 2];
|
||||
|
||||
if (!(ref & 1))
|
||||
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, val);
|
||||
else
|
||||
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_ref_prio_set - set reference priority
|
||||
* @chan: pointer to channel state
|
||||
* @ref: input reference index
|
||||
* @prio: priority to set
|
||||
*/
|
||||
static inline void
|
||||
zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
|
||||
{
|
||||
u8 *val = &chan->ref_prio[ref / 2];
|
||||
|
||||
if (!(ref & 1))
|
||||
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_P, val, prio);
|
||||
else
|
||||
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_N, val, prio);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_ref_is_selectable - check if reference is selectable
|
||||
* @chan: pointer to channel state
|
||||
* @ref: input reference index
|
||||
*
|
||||
* Return: true if the reference priority is not NONE, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_chan_ref_is_selectable(const struct zl3073x_chan *chan, u8 ref)
|
||||
{
|
||||
return zl3073x_chan_ref_prio_get(chan, ref) != ZL_DPLL_REF_PRIO_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_lock_state_get - get DPLL channel lock state
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: lock state of the given DPLL channel
|
||||
*/
|
||||
static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_is_ho_ready - check if holdover is ready
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: true if holdover is ready, false otherwise
|
||||
*/
|
||||
static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_refsel_state_get - get reference selection state
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: reference selection state of the given DPLL channel
|
||||
*/
|
||||
static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
|
||||
* @chan: pointer to channel state
|
||||
*
|
||||
* Return: reference selected by the DPLL in automatic mode
|
||||
*/
|
||||
static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
|
||||
{
|
||||
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
|
||||
}
|
||||
|
||||
#endif /* _ZL3073X_CHAN_H */
|
||||
@ -20,78 +20,29 @@
|
||||
#include "dpll.h"
|
||||
#include "regs.h"
|
||||
|
||||
/* Chip IDs for zl30731 */
|
||||
static const u16 zl30731_ids[] = {
|
||||
0x0E93,
|
||||
0x1E93,
|
||||
0x2E93,
|
||||
};
|
||||
#define ZL_CHIP_INFO(_id, _nchannels, _flags) \
|
||||
{ .id = (_id), .num_channels = (_nchannels), .flags = (_flags) }
|
||||
|
||||
const struct zl3073x_chip_info zl30731_chip_info = {
|
||||
.ids = zl30731_ids,
|
||||
.num_ids = ARRAY_SIZE(zl30731_ids),
|
||||
.num_channels = 1,
|
||||
static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
|
||||
ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, ZL3073X);
|
||||
|
||||
/* Chip IDs for zl30732 */
|
||||
static const u16 zl30732_ids[] = {
|
||||
0x0E30,
|
||||
0x0E94,
|
||||
0x1E94,
|
||||
0x1F60,
|
||||
0x2E94,
|
||||
0x3FC4,
|
||||
};
|
||||
|
||||
const struct zl3073x_chip_info zl30732_chip_info = {
|
||||
.ids = zl30732_ids,
|
||||
.num_ids = ARRAY_SIZE(zl30732_ids),
|
||||
.num_channels = 2,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, ZL3073X);
|
||||
|
||||
/* Chip IDs for zl30733 */
|
||||
static const u16 zl30733_ids[] = {
|
||||
0x0E95,
|
||||
0x1E95,
|
||||
0x2E95,
|
||||
};
|
||||
|
||||
const struct zl3073x_chip_info zl30733_chip_info = {
|
||||
.ids = zl30733_ids,
|
||||
.num_ids = ARRAY_SIZE(zl30733_ids),
|
||||
.num_channels = 3,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, ZL3073X);
|
||||
|
||||
/* Chip IDs for zl30734 */
|
||||
static const u16 zl30734_ids[] = {
|
||||
0x0E96,
|
||||
0x1E96,
|
||||
0x2E96,
|
||||
};
|
||||
|
||||
const struct zl3073x_chip_info zl30734_chip_info = {
|
||||
.ids = zl30734_ids,
|
||||
.num_ids = ARRAY_SIZE(zl30734_ids),
|
||||
.num_channels = 4,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, ZL3073X);
|
||||
|
||||
/* Chip IDs for zl30735 */
|
||||
static const u16 zl30735_ids[] = {
|
||||
0x0E97,
|
||||
0x1E97,
|
||||
0x2E97,
|
||||
};
|
||||
|
||||
const struct zl3073x_chip_info zl30735_chip_info = {
|
||||
.ids = zl30735_ids,
|
||||
.num_ids = ARRAY_SIZE(zl30735_ids),
|
||||
.num_channels = 5,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, ZL3073X);
|
||||
|
||||
#define ZL_RANGE_OFFSET 0x80
|
||||
#define ZL_PAGE_SIZE 0x80
|
||||
@ -588,17 +539,26 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
rc = zl3073x_chan_state_fetch(zldev, i);
|
||||
if (rc) {
|
||||
dev_err(zldev->dev,
|
||||
"Failed to fetch channel state: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
||||
zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
|
||||
&zldev->ref[i].mon_status);
|
||||
rc = zl3073x_ref_state_update(zldev, i);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to get REF%u status: %pe\n", i,
|
||||
@ -606,6 +566,20 @@ zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
rc = zl3073x_chan_state_update(zldev, i);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to get DPLL%u state: %pe\n", i,
|
||||
ERR_PTR(rc));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_phase_offsets_update - update reference phase offsets
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@ -658,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
* zl3073x_ref_freq_meas_latch - latch reference frequency measurements
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
|
||||
*
|
||||
* The function asks device to update fractional frequency offsets latch
|
||||
* registers the latest measured values, reads and stores them into
|
||||
* The function waits for the previous measurement to finish, selects all
|
||||
* references and requests a new measurement of the given type.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
|
||||
{
|
||||
int i, rc;
|
||||
int rc;
|
||||
|
||||
/* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
|
||||
* to ensure that the measured data are coherent.
|
||||
*/
|
||||
/* Wait for previous measurement to finish */
|
||||
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
if (rc)
|
||||
@ -689,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Request frequency offset measurement */
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
/* Request measurement */
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Wait for finish */
|
||||
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_meas_update - update measured input reference frequencies
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
*
|
||||
* The function asks device to latch measured input reference frequencies
|
||||
* and stores the results in the ref state.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
|
||||
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
u32 value;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
zldev->ref[i].meas_freq = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
*
|
||||
* The function asks device to latch the latest measured fractional
|
||||
* frequency offset values, reads and stores them into the ref state.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
rc = zl3073x_ref_freq_meas_latch(zldev,
|
||||
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -728,8 +750,11 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
struct zl3073x_dpll *zldpll;
|
||||
int rc;
|
||||
|
||||
/* Update input references status */
|
||||
zl3073x_dev_ref_status_update(zldev);
|
||||
/* Update input references' states */
|
||||
zl3073x_dev_ref_states_update(zldev);
|
||||
|
||||
/* Update DPLL channels' states */
|
||||
zl3073x_dev_chan_states_update(zldev);
|
||||
|
||||
/* Update DPLL-to-connected-ref phase offsets registers */
|
||||
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
|
||||
@ -737,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
|
||||
/* Update measured input reference frequencies if any DPLL has
|
||||
* frequency monitoring enabled.
|
||||
*/
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list) {
|
||||
if (zldpll->freq_monitor) {
|
||||
rc = zl3073x_ref_freq_meas_update(zldev);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to update measured frequencies: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update references' fractional frequency offsets */
|
||||
rc = zl3073x_ref_ffo_update(zldev);
|
||||
if (rc)
|
||||
@ -766,8 +805,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
|
||||
value = (factor + 1) & 0x0f;
|
||||
|
||||
/* Update phase measurement control register */
|
||||
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
|
||||
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
|
||||
FIELD_MODIFY(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, &dpll_meas_ctrl, value);
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -942,7 +980,7 @@ static void zl3073x_dev_dpll_fini(void *ptr)
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)
|
||||
{
|
||||
struct kthread_worker *kworker;
|
||||
struct zl3073x_dpll *zldpll;
|
||||
@ -952,7 +990,7 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
INIT_LIST_HEAD(&zldev->dplls);
|
||||
|
||||
/* Allocate all DPLLs */
|
||||
for (i = 0; i < num_dplls; i++) {
|
||||
for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
zldpll = zl3073x_dpll_alloc(zldev, i);
|
||||
if (IS_ERR(zldpll)) {
|
||||
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
|
||||
@ -994,14 +1032,12 @@ error:
|
||||
/**
|
||||
* zl3073x_dev_probe - initialize zl3073x device
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @chip_info: chip info based on compatible
|
||||
*
|
||||
* Common initialization of zl3073x device structure.
|
||||
*
|
||||
* Returns: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
const struct zl3073x_chip_info *chip_info)
|
||||
int zl3073x_dev_probe(struct zl3073x_dev *zldev)
|
||||
{
|
||||
u16 id, revision, fw_ver;
|
||||
unsigned int i;
|
||||
@ -1013,18 +1049,17 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check it matches */
|
||||
for (i = 0; i < chip_info->num_ids; i++) {
|
||||
if (id == chip_info->ids[i])
|
||||
/* Detect chip variant */
|
||||
for (i = 0; i < ARRAY_SIZE(zl3073x_chip_ids); i++) {
|
||||
if (zl3073x_chip_ids[i].id == id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == chip_info->num_ids) {
|
||||
if (i == ARRAY_SIZE(zl3073x_chip_ids))
|
||||
return dev_err_probe(zldev->dev, -ENODEV,
|
||||
"Unknown or non-match chip ID: 0x%0x\n",
|
||||
id);
|
||||
}
|
||||
zldev->chip_id = id;
|
||||
"Unknown chip ID: 0x%04x\n", id);
|
||||
|
||||
zldev->info = &zl3073x_chip_ids[i];
|
||||
|
||||
/* Read revision, firmware version and custom config version */
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
|
||||
@ -1063,7 +1098,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
"Failed to initialize mutex\n");
|
||||
|
||||
/* Register DPLL channels */
|
||||
rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
|
||||
rc = zl3073x_devm_dpll_init(zldev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "chan.h"
|
||||
#include "out.h"
|
||||
#include "ref.h"
|
||||
#include "regs.h"
|
||||
@ -18,27 +19,39 @@ struct device;
|
||||
struct regmap;
|
||||
struct zl3073x_dpll;
|
||||
|
||||
/*
|
||||
* Hardware limits for ZL3073x chip family
|
||||
|
||||
enum zl3073x_flags {
|
||||
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
||||
ZL3073X_FLAG_DIE_TEMP_BIT,
|
||||
ZL3073X_FLAGS_NBITS /* must be last */
|
||||
};
|
||||
|
||||
#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
|
||||
#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
|
||||
#define ZL3073X_FLAG_DIE_TEMP __ZL3073X_FLAG(DIE_TEMP)
|
||||
|
||||
/**
|
||||
* struct zl3073x_chip_info - chip variant identification
|
||||
* @id: chip ID
|
||||
* @num_channels: number of DPLL channels supported by this variant
|
||||
* @flags: chip variant flags
|
||||
*/
|
||||
#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_chip_info {
|
||||
u16 id;
|
||||
u8 num_channels;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zl3073x_dev - zl3073x device
|
||||
* @dev: pointer to device
|
||||
* @regmap: regmap to access device registers
|
||||
* @info: detected chip info
|
||||
* @multiop_lock: to serialize multiple register operations
|
||||
* @chip_id: chip ID read from hardware
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
* @chan: array of DPLL channels' state
|
||||
* @dplls: list of DPLLs
|
||||
* @kworker: thread for periodic work
|
||||
* @work: periodic work
|
||||
@ -46,15 +59,16 @@ struct zl3073x_dpll;
|
||||
* @phase_avg_factor: phase offset measurement averaging factor
|
||||
*/
|
||||
struct zl3073x_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex multiop_lock;
|
||||
u16 chip_id;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
const struct zl3073x_chip_info *info;
|
||||
struct mutex multiop_lock;
|
||||
|
||||
/* Invariants */
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
struct zl3073x_out out[ZL3073X_NUM_OUTS];
|
||||
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
|
||||
struct zl3073x_chan chan[ZL3073X_MAX_CHANNELS];
|
||||
|
||||
/* DPLL channels */
|
||||
struct list_head dplls;
|
||||
@ -68,22 +82,10 @@ struct zl3073x_dev {
|
||||
u8 phase_avg_factor;
|
||||
};
|
||||
|
||||
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_probe(struct zl3073x_dev *zldev);
|
||||
|
||||
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
|
||||
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
|
||||
@ -158,18 +160,7 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
|
||||
static inline bool
|
||||
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
|
||||
{
|
||||
switch (zldev->chip_id) {
|
||||
case 0x0E30:
|
||||
case 0x0E93:
|
||||
case 0x0E94:
|
||||
case 0x0E95:
|
||||
case 0x0E96:
|
||||
case 0x0E97:
|
||||
case 0x1F60:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return zldev->info->flags & ZL3073X_FLAG_REF_PHASE_COMP_32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -13,10 +13,10 @@
|
||||
* @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
|
||||
* @freq_monitor: is frequency monitor enabled
|
||||
* @ops: DPLL device operations for this instance
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @tracker: tracking object for the acquired reference
|
||||
* @lock_status: last saved DPLL lock status
|
||||
@ -27,10 +27,10 @@ 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;
|
||||
bool freq_monitor;
|
||||
struct dpll_device_ops ops;
|
||||
struct dpll_device *dpll_dev;
|
||||
dpll_tracker tracker;
|
||||
enum dpll_lock_status lock_status;
|
||||
|
||||
@ -194,8 +194,7 @@ zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
value &= ~ZL_WRITE_FLASH_OP;
|
||||
value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
|
||||
FIELD_MODIFY(ZL_WRITE_FLASH_OP, &value, operation);
|
||||
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
|
||||
if (rc)
|
||||
|
||||
@ -22,40 +22,25 @@ static int zl3073x_i2c_probe(struct i2c_client *client)
|
||||
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
|
||||
"Failed to initialize regmap\n");
|
||||
|
||||
return zl3073x_dev_probe(zldev, i2c_get_match_data(client));
|
||||
return zl3073x_dev_probe(zldev);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
{ "zl30731" },
|
||||
{ "zl30732" },
|
||||
{ "zl30733" },
|
||||
{ "zl30734" },
|
||||
{ "zl30735" },
|
||||
{ /* 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 },
|
||||
{ .compatible = "microchip,zl30731" },
|
||||
{ .compatible = "microchip,zl30732" },
|
||||
{ .compatible = "microchip,zl30733" },
|
||||
{ .compatible = "microchip,zl30734" },
|
||||
{ .compatible = "microchip,zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match);
|
||||
|
||||
@ -106,12 +106,32 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
return &zldev->out[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_state_set - commit output state changes to hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: output index to set state for
|
||||
* @out: desired output state
|
||||
*
|
||||
* Validates that invariant fields have not been modified, skips the HW
|
||||
* write if the mutable configuration is unchanged, and otherwise writes
|
||||
* only the changed cfg fields to hardware via the mailbox interface.
|
||||
*
|
||||
* Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
||||
*/
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out)
|
||||
{
|
||||
struct zl3073x_out *dout = &zldev->out[index];
|
||||
int rc;
|
||||
|
||||
/* Reject attempts to change invariant fields (set at fetch only) */
|
||||
if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv))))
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip HW write if configuration hasn't changed */
|
||||
if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg)))
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
@ -146,12 +166,7 @@ int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
return rc;
|
||||
|
||||
/* After successful commit store new state */
|
||||
dout->div = out->div;
|
||||
dout->width = out->width;
|
||||
dout->esync_n_period = out->esync_n_period;
|
||||
dout->esync_n_width = out->esync_n_width;
|
||||
dout->mode = out->mode;
|
||||
dout->phase_comp = out->phase_comp;
|
||||
dout->cfg = out->cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#define _ZL3073X_OUT_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@ -17,17 +18,21 @@ struct zl3073x_dev;
|
||||
* @esync_n_period: embedded sync or n-pin period (for n-div formats)
|
||||
* @esync_n_width: embedded sync or n-pin pulse width
|
||||
* @phase_comp: phase compensation
|
||||
* @ctrl: output control
|
||||
* @mode: output mode
|
||||
* @ctrl: output control
|
||||
*/
|
||||
struct zl3073x_out {
|
||||
u32 div;
|
||||
u32 width;
|
||||
u32 esync_n_period;
|
||||
u32 esync_n_width;
|
||||
s32 phase_comp;
|
||||
u8 ctrl;
|
||||
u8 mode;
|
||||
struct_group(cfg, /* Config */
|
||||
u32 div;
|
||||
u32 width;
|
||||
u32 esync_n_period;
|
||||
u32 esync_n_width;
|
||||
s32 phase_comp;
|
||||
u8 mode;
|
||||
);
|
||||
struct_group(inv, /* Invariants */
|
||||
u8 ctrl;
|
||||
);
|
||||
};
|
||||
|
||||
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
@ -37,6 +42,28 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out);
|
||||
|
||||
/**
|
||||
* zl3073x_out_clock_type_get - get output clock type
|
||||
* @out: pointer to out state
|
||||
*
|
||||
* Return: clock type of given output (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
||||
*/
|
||||
static inline u8 zl3073x_out_clock_type_get(const struct zl3073x_out *out)
|
||||
{
|
||||
return FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_clock_type_set - set output clock type
|
||||
* @out: pointer to out state
|
||||
* @type: clock type (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
||||
*/
|
||||
static inline void
|
||||
zl3073x_out_clock_type_set(struct zl3073x_out *out, u8 type)
|
||||
{
|
||||
FIELD_MODIFY(ZL_OUTPUT_MODE_CLOCK_TYPE, &out->mode, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_signal_format_get - get output signal format
|
||||
* @out: pointer to out state
|
||||
|
||||
@ -51,6 +51,21 @@ zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_state_update - update input reference status from HW
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: input reference index
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_ref *ref = &zldev->ref[index];
|
||||
|
||||
return zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(index),
|
||||
&ref->mon_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_state_fetch - fetch input reference state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@ -73,18 +88,17 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
|
||||
|
||||
/* Copy the shared items from the P-pin */
|
||||
ref->config = p_ref->config;
|
||||
ref->esync_n_div = p_ref->esync_n_div;
|
||||
ref->freq_base = p_ref->freq_base;
|
||||
ref->freq_mult = p_ref->freq_mult;
|
||||
ref->freq_ratio_m = p_ref->freq_ratio_m;
|
||||
ref->freq_ratio_n = p_ref->freq_ratio_n;
|
||||
ref->phase_comp = p_ref->phase_comp;
|
||||
ref->sync_ctrl = p_ref->sync_ctrl;
|
||||
ref->cfg = p_ref->cfg;
|
||||
ref->inv = p_ref->inv;
|
||||
|
||||
return 0; /* Finish - no non-shared items for now */
|
||||
}
|
||||
|
||||
/* Read reference status */
|
||||
rc = zl3073x_ref_state_update(zldev, index);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read reference configuration */
|
||||
@ -154,12 +168,32 @@ zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
|
||||
return &zldev->ref[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_state_set - commit input reference state changes to hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: input reference index to set state for
|
||||
* @ref: desired reference state
|
||||
*
|
||||
* Validates that invariant fields have not been modified, skips the HW
|
||||
* write if the mutable configuration is unchanged, and otherwise writes
|
||||
* only the changed cfg fields to hardware via the mailbox interface.
|
||||
*
|
||||
* Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
||||
*/
|
||||
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_ref *ref)
|
||||
{
|
||||
struct zl3073x_ref *dref = &zldev->ref[index];
|
||||
int rc;
|
||||
|
||||
/* Reject attempts to change invariant fields (set at init only) */
|
||||
if (WARN_ON(memcmp(&dref->inv, &ref->inv, sizeof(ref->inv))))
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip HW write if configuration hasn't changed */
|
||||
if (!memcmp(&dref->cfg, &ref->cfg, sizeof(ref->cfg)))
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read reference configuration into mailbox */
|
||||
@ -207,13 +241,7 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
return rc;
|
||||
|
||||
/* After successful commit store new state */
|
||||
dref->freq_base = ref->freq_base;
|
||||
dref->freq_mult = ref->freq_mult;
|
||||
dref->freq_ratio_m = ref->freq_ratio_m;
|
||||
dref->freq_ratio_n = ref->freq_ratio_n;
|
||||
dref->esync_n_div = ref->esync_n_div;
|
||||
dref->sync_ctrl = ref->sync_ctrl;
|
||||
dref->phase_comp = ref->phase_comp;
|
||||
dref->cfg = ref->cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@ -13,28 +14,36 @@ struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_ref - input reference state
|
||||
* @ffo: current fractional frequency offset
|
||||
* @phase_comp: phase compensation
|
||||
* @esync_n_div: divisor for embedded sync or n-divided signal formats
|
||||
* @freq_base: frequency base
|
||||
* @freq_mult: frequnecy multiplier
|
||||
* @freq_ratio_m: FEC mode multiplier
|
||||
* @freq_ratio_n: FEC mode divisor
|
||||
* @config: reference config
|
||||
* @sync_ctrl: reference sync control
|
||||
* @config: reference config
|
||||
* @ffo: current fractional frequency offset
|
||||
* @meas_freq: measured input frequency in Hz
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
struct zl3073x_ref {
|
||||
s64 ffo;
|
||||
u64 phase_comp;
|
||||
u32 esync_n_div;
|
||||
u16 freq_base;
|
||||
u16 freq_mult;
|
||||
u16 freq_ratio_m;
|
||||
u16 freq_ratio_n;
|
||||
u8 config;
|
||||
u8 sync_ctrl;
|
||||
u8 mon_status;
|
||||
struct_group(cfg, /* Configuration */
|
||||
u64 phase_comp;
|
||||
u32 esync_n_div;
|
||||
u16 freq_base;
|
||||
u16 freq_mult;
|
||||
u16 freq_ratio_m;
|
||||
u16 freq_ratio_n;
|
||||
u8 sync_ctrl;
|
||||
);
|
||||
struct_group(inv, /* Invariants */
|
||||
u8 config;
|
||||
);
|
||||
struct_group(stat, /* Status */
|
||||
s64 ffo;
|
||||
u32 meas_freq;
|
||||
u8 mon_status;
|
||||
);
|
||||
};
|
||||
|
||||
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
@ -45,6 +54,8 @@ const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
|
||||
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_ref *ref);
|
||||
|
||||
int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
|
||||
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
|
||||
|
||||
/**
|
||||
@ -59,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
||||
return ref->ffo;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_meas_freq_get - get measured input frequency
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: measured input frequency in Hz
|
||||
*/
|
||||
static inline u32
|
||||
zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return ref->meas_freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_get - get given input reference frequency
|
||||
* @ref: pointer to ref state
|
||||
@ -97,6 +120,52 @@ zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_sync_mode_get - get sync control mode
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_ref_sync_mode_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_sync_mode_set - set sync control mode
|
||||
* @ref: pointer to ref state
|
||||
* @mode: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
||||
*/
|
||||
static inline void
|
||||
zl3073x_ref_sync_mode_set(struct zl3073x_ref *ref, u8 mode)
|
||||
{
|
||||
FIELD_MODIFY(ZL_REF_SYNC_CTRL_MODE, &ref->sync_ctrl, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_sync_pair_get - get sync pair reference index
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: paired reference index
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_ref_sync_pair_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return FIELD_GET(ZL_REF_SYNC_CTRL_PAIR, ref->sync_ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_sync_pair_set - set sync pair reference index
|
||||
* @ref: pointer to ref state
|
||||
* @pair: paired reference index
|
||||
*/
|
||||
static inline void
|
||||
zl3073x_ref_sync_pair_set(struct zl3073x_ref *ref, u8 pair)
|
||||
{
|
||||
FIELD_MODIFY(ZL_REF_SYNC_CTRL_PAIR, &ref->sync_ctrl, pair);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_diff - check if the given input reference is differential
|
||||
* @ref: pointer to ref state
|
||||
|
||||
@ -6,6 +6,18 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
/*
|
||||
* Register address structure:
|
||||
* ===========================
|
||||
@ -78,6 +90,8 @@
|
||||
#define ZL_REG_RESET_STATUS ZL_REG(0, 0x18, 1)
|
||||
#define ZL_REG_RESET_STATUS_RESET BIT(0)
|
||||
|
||||
#define ZL_REG_DIE_TEMP_STATUS ZL_REG(0, 0x44, 2)
|
||||
|
||||
/*************************
|
||||
* Register Page 2, Status
|
||||
*************************/
|
||||
@ -199,7 +213,9 @@
|
||||
#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_REFSYNC_PAIR 1
|
||||
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
|
||||
#define ZL_REF_SYNC_CTRL_PAIR GENMASK(7, 4)
|
||||
|
||||
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
|
||||
#define ZL_REF_ESYNC_DIV_1HZ 0
|
||||
|
||||
@ -22,40 +22,25 @@ static int zl3073x_spi_probe(struct spi_device *spi)
|
||||
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));
|
||||
return zl3073x_dev_probe(zldev);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
{ "zl30731" },
|
||||
{ "zl30732" },
|
||||
{ "zl30733" },
|
||||
{ "zl30734" },
|
||||
{ "zl30735" },
|
||||
{ /* 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 },
|
||||
{ .compatible = "microchip,zl30731" },
|
||||
{ .compatible = "microchip,zl30732" },
|
||||
{ .compatible = "microchip,zl30733" },
|
||||
{ .compatible = "microchip,zl30734" },
|
||||
{ .compatible = "microchip,zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@ -20,11 +21,13 @@ struct zl3073x_dev;
|
||||
* @ctrl: synth control
|
||||
*/
|
||||
struct zl3073x_synth {
|
||||
u32 freq_mult;
|
||||
u16 freq_base;
|
||||
u16 freq_m;
|
||||
u16 freq_n;
|
||||
u8 ctrl;
|
||||
struct_group(inv, /* Invariants */
|
||||
u32 freq_mult;
|
||||
u16 freq_base;
|
||||
u16 freq_m;
|
||||
u16 freq_n;
|
||||
u8 ctrl;
|
||||
);
|
||||
};
|
||||
|
||||
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
||||
@ -32,9 +35,6 @@ int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
||||
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
|
||||
u8 synth_id);
|
||||
|
||||
int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
|
||||
const struct zl3073x_synth *synth);
|
||||
|
||||
/**
|
||||
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
|
||||
* @synth: pointer to synth state
|
||||
|
||||
@ -77,9 +77,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
srq->rq.queue = q;
|
||||
init->attr.max_wr = srq->rq.max_wr;
|
||||
|
||||
if (uresp) {
|
||||
if (copy_to_user(&uresp->srq_num, &srq->srq_num,
|
||||
sizeof(uresp->srq_num))) {
|
||||
@ -88,6 +85,9 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
|
||||
}
|
||||
}
|
||||
|
||||
srq->rq.queue = q;
|
||||
init->attr.max_wr = srq->rq.max_wr;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
|
||||
@ -5398,7 +5398,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
|
||||
if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
|
||||
continue;
|
||||
|
||||
if (bond_is_last_slave(bond, slave)) {
|
||||
if (i + 1 == slaves_count) {
|
||||
skb2 = skb;
|
||||
skb_used = true;
|
||||
} else {
|
||||
|
||||
@ -43,7 +43,8 @@ ice-y := ice_main.o \
|
||||
ice_repr.o \
|
||||
ice_tc_lib.o \
|
||||
ice_debugfs.o \
|
||||
ice_adapter.o
|
||||
ice_adapter.o \
|
||||
ice_rh.o
|
||||
ice-$(CONFIG_PCI_IOV) += \
|
||||
ice_sriov.o \
|
||||
virt/allowlist.o \
|
||||
|
||||
@ -837,6 +837,28 @@ static inline void ice_tx_xsk_pool(struct ice_vsi *vsi, u16 qid)
|
||||
WRITE_ONCE(ring->xsk_pool, ice_get_xp_from_qid(vsi, qid));
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_max_txq - return the maximum number of Tx queues for in a PF
|
||||
* @pf: PF structure
|
||||
*
|
||||
* Return: maximum number of Tx queues
|
||||
*/
|
||||
static inline int ice_get_max_txq(struct ice_pf *pf)
|
||||
{
|
||||
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_txq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_max_rxq - return the maximum number of Rx queues for in a PF
|
||||
* @pf: PF structure
|
||||
*
|
||||
* Return: maximum number of Rx queues
|
||||
*/
|
||||
static inline int ice_get_max_rxq(struct ice_pf *pf)
|
||||
{
|
||||
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_rxq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_main_vsi - Get the PF VSI
|
||||
* @pf: PF instance
|
||||
|
||||
@ -3744,24 +3744,6 @@ ice_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_max_txq - return the maximum number of Tx queues for in a PF
|
||||
* @pf: PF structure
|
||||
*/
|
||||
static int ice_get_max_txq(struct ice_pf *pf)
|
||||
{
|
||||
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_txq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_max_rxq - return the maximum number of Rx queues for in a PF
|
||||
* @pf: PF structure
|
||||
*/
|
||||
static int ice_get_max_rxq(struct ice_pf *pf)
|
||||
{
|
||||
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_rxq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_combined_cnt - return the current number of combined channels
|
||||
* @vsi: PF VSI pointer
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "ice.h"
|
||||
#include "ice_lib.h"
|
||||
#include "ice_irq.h"
|
||||
#include "ice_rh.h"
|
||||
|
||||
/**
|
||||
* ice_init_irq_tracker - initialize interrupt tracker
|
||||
@ -106,9 +107,10 @@ static struct ice_irq_entry *ice_get_irq_res(struct ice_pf *pf,
|
||||
#define ICE_RDMA_AEQ_MSIX 1
|
||||
static int ice_get_default_msix_amount(struct ice_pf *pf)
|
||||
{
|
||||
return ICE_MIN_LAN_OICR_MSIX + num_online_cpus() +
|
||||
return ICE_MIN_LAN_OICR_MSIX + ice_rh_get_num_default_rss_queues() +
|
||||
(test_bit(ICE_FLAG_FD_ENA, pf->flags) ? ICE_FDIR_MSIX : 0) +
|
||||
(ice_is_rdma_ena(pf) ? num_online_cpus() + ICE_RDMA_AEQ_MSIX : 0);
|
||||
(ice_is_rdma_ena(pf) ? ice_rh_get_num_default_rss_queues() +
|
||||
ICE_RDMA_AEQ_MSIX : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "ice_dcb_lib.h"
|
||||
#include "ice_type.h"
|
||||
#include "ice_vsi_vlan_ops.h"
|
||||
#include "ice_rh.h"
|
||||
|
||||
/**
|
||||
* ice_vsi_type_str - maps VSI type enum to string equivalents
|
||||
@ -159,12 +160,14 @@ static void ice_vsi_set_num_desc(struct ice_vsi *vsi)
|
||||
|
||||
static u16 ice_get_rxq_count(struct ice_pf *pf)
|
||||
{
|
||||
return min(ice_get_avail_rxq_count(pf), num_online_cpus());
|
||||
return min(ice_get_avail_rxq_count(pf),
|
||||
ice_rh_get_num_default_rss_queues());
|
||||
}
|
||||
|
||||
static u16 ice_get_txq_count(struct ice_pf *pf)
|
||||
{
|
||||
return min(ice_get_avail_txq_count(pf), num_online_cpus());
|
||||
return min(ice_get_avail_txq_count(pf),
|
||||
ice_rh_get_num_default_rss_queues());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -911,13 +914,15 @@ static void ice_vsi_set_rss_params(struct ice_vsi *vsi)
|
||||
if (vsi->type == ICE_VSI_CHNL)
|
||||
vsi->rss_size = min_t(u16, vsi->num_rxq, max_rss_size);
|
||||
else
|
||||
vsi->rss_size = min_t(u16, num_online_cpus(),
|
||||
vsi->rss_size = min_t(u16,
|
||||
ice_rh_get_num_default_rss_queues(),
|
||||
max_rss_size);
|
||||
vsi->rss_lut_type = ICE_LUT_PF;
|
||||
break;
|
||||
case ICE_VSI_SF:
|
||||
vsi->rss_table_size = ICE_LUT_VSI_SIZE;
|
||||
vsi->rss_size = min_t(u16, num_online_cpus(), max_rss_size);
|
||||
vsi->rss_size = min_t(u16, ice_rh_get_num_default_rss_queues(),
|
||||
max_rss_size);
|
||||
vsi->rss_lut_type = ICE_LUT_VSI;
|
||||
break;
|
||||
case ICE_VSI_VF:
|
||||
@ -3007,7 +3012,7 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi,
|
||||
* ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones
|
||||
* @vsi: VSI pointer
|
||||
*/
|
||||
static int
|
||||
int
|
||||
ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
|
||||
{
|
||||
u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq;
|
||||
|
||||
@ -66,6 +66,7 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked);
|
||||
void ice_vsi_decfg(struct ice_vsi *vsi);
|
||||
void ice_dis_vsi(struct ice_vsi *vsi, bool locked);
|
||||
|
||||
int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi);
|
||||
int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags);
|
||||
int ice_vsi_cfg(struct ice_vsi *vsi);
|
||||
struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf);
|
||||
|
||||
@ -4730,8 +4730,8 @@ static int ice_cfg_netdev(struct ice_vsi *vsi)
|
||||
struct net_device *netdev;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
|
||||
netdev = alloc_etherdev_mqs(sizeof(*np), vsi->alloc_txq,
|
||||
vsi->alloc_rxq);
|
||||
netdev = alloc_etherdev_mqs(sizeof(*np), ice_get_max_txq(vsi->back),
|
||||
ice_get_max_rxq(vsi->back));
|
||||
if (!netdev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
33
drivers/net/ethernet/intel/ice/ice_rh.c
Normal file
33
drivers/net/ethernet/intel/ice/ice_rh.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/topology.h>
|
||||
|
||||
#include "ice_rh.h"
|
||||
|
||||
/**
|
||||
* ice_rh_get_num_default_rss_queues - default number of RSS queues
|
||||
*
|
||||
* Default value is the number of physical cores if there are only 1 or 2, or
|
||||
* divided by 2 if there are more.
|
||||
*
|
||||
* This is a renamed copy of netif_get_num_default_rss_queues() from upstream.
|
||||
*/
|
||||
int ice_rh_get_num_default_rss_queues(void)
|
||||
{
|
||||
cpumask_var_t cpus;
|
||||
int cpu, count = 0;
|
||||
|
||||
if (unlikely(is_kdump_kernel() || !zalloc_cpumask_var(&cpus, GFP_KERNEL)))
|
||||
return 1;
|
||||
|
||||
cpumask_copy(cpus, cpu_online_mask);
|
||||
for_each_cpu(cpu, cpus) {
|
||||
++count;
|
||||
cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu));
|
||||
}
|
||||
free_cpumask_var(cpus);
|
||||
|
||||
return count > 2 ? DIV_ROUND_UP(count, 2) : count;
|
||||
}
|
||||
6
drivers/net/ethernet/intel/ice/ice_rh.h
Normal file
6
drivers/net/ethernet/intel/ice/ice_rh.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __ICE_RH_H
|
||||
#define __ICE_RH_H
|
||||
|
||||
int ice_rh_get_num_default_rss_queues(void);
|
||||
|
||||
#endif
|
||||
@ -268,6 +268,13 @@ static int ice_vf_reconfig_vsi(struct ice_vf *vf)
|
||||
|
||||
vsi->flags = ICE_VSI_FLAG_NO_INIT;
|
||||
|
||||
vsi->req_txq = vf->num_req_qs;
|
||||
vsi->req_rxq = vf->num_req_qs;
|
||||
|
||||
err = ice_vsi_realloc_stat_arrays(vsi);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ice_vsi_decfg(vsi);
|
||||
ice_fltr_remove_all(vsi);
|
||||
|
||||
|
||||
@ -44,13 +44,14 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type)
|
||||
{
|
||||
struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
|
||||
struct mlx4_srq *srq;
|
||||
unsigned long flags;
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irqsave(&srq_table->lock, flags);
|
||||
srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1));
|
||||
rcu_read_unlock();
|
||||
if (srq)
|
||||
refcount_inc(&srq->refcount);
|
||||
else {
|
||||
if (!srq || !refcount_inc_not_zero(&srq->refcount))
|
||||
srq = NULL;
|
||||
spin_unlock_irqrestore(&srq_table->lock, flags);
|
||||
if (!srq) {
|
||||
mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn);
|
||||
return;
|
||||
}
|
||||
@ -203,8 +204,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
|
||||
if (err)
|
||||
goto err_radix;
|
||||
|
||||
refcount_set(&srq->refcount, 1);
|
||||
init_completion(&srq->free);
|
||||
atomic_set_release(&srq->refcount.refs, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@ -3196,6 +3196,7 @@ static int add_adev(struct gdma_dev *gd, const char *name)
|
||||
struct auxiliary_device *adev;
|
||||
struct mana_adev *madev;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
madev = kzalloc(sizeof(*madev), GFP_KERNEL);
|
||||
if (!madev)
|
||||
@ -3205,7 +3206,8 @@ static int add_adev(struct gdma_dev *gd, const char *name)
|
||||
ret = mana_adev_idx_alloc();
|
||||
if (ret < 0)
|
||||
goto idx_fail;
|
||||
adev->id = ret;
|
||||
id = ret;
|
||||
adev->id = id;
|
||||
|
||||
adev->name = name;
|
||||
adev->dev.parent = gd->gdma_context->dev;
|
||||
@ -3231,7 +3233,7 @@ add_fail:
|
||||
auxiliary_device_uninit(adev);
|
||||
|
||||
init_fail:
|
||||
mana_adev_idx_free(adev->id);
|
||||
mana_adev_idx_free(id);
|
||||
|
||||
idx_fail:
|
||||
kfree(madev);
|
||||
|
||||
@ -2354,17 +2354,11 @@ nvme_fc_ctrl_free(struct kref *ref)
|
||||
container_of(ref, struct nvme_fc_ctrl, ref);
|
||||
unsigned long flags;
|
||||
|
||||
if (ctrl->ctrl.tagset)
|
||||
nvme_remove_io_tag_set(&ctrl->ctrl);
|
||||
|
||||
/* remove from rport list */
|
||||
spin_lock_irqsave(&ctrl->rport->lock, flags);
|
||||
list_del(&ctrl->ctrl_list);
|
||||
spin_unlock_irqrestore(&ctrl->rport->lock, flags);
|
||||
|
||||
nvme_unquiesce_admin_queue(&ctrl->ctrl);
|
||||
nvme_remove_admin_tag_set(&ctrl->ctrl);
|
||||
|
||||
kfree(ctrl->queues);
|
||||
|
||||
put_device(ctrl->dev);
|
||||
@ -3250,13 +3244,20 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
|
||||
{
|
||||
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
|
||||
|
||||
cancel_work_sync(&ctrl->ioerr_work);
|
||||
cancel_delayed_work_sync(&ctrl->connect_work);
|
||||
|
||||
/*
|
||||
* kill the association on the link side. this will block
|
||||
* waiting for io to terminate
|
||||
*/
|
||||
nvme_fc_delete_association(ctrl);
|
||||
cancel_work_sync(&ctrl->ioerr_work);
|
||||
|
||||
if (ctrl->ctrl.tagset)
|
||||
nvme_remove_io_tag_set(&ctrl->ctrl);
|
||||
|
||||
nvme_unquiesce_admin_queue(&ctrl->ctrl);
|
||||
nvme_remove_admin_tag_set(&ctrl->ctrl);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@ -6144,10 +6144,12 @@ static void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busi
|
||||
static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid,
|
||||
char *sec_busid)
|
||||
{
|
||||
struct dasd_eckd_private *prim_priv, *sec_priv;
|
||||
struct dasd_device *primary, *secondary;
|
||||
struct dasd_copy_relation *copy;
|
||||
struct dasd_block *block;
|
||||
struct gendisk *gdp;
|
||||
int rc;
|
||||
|
||||
copy = device->copy;
|
||||
if (!copy)
|
||||
@ -6163,6 +6165,9 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
|
||||
if (!secondary)
|
||||
return DASD_COPYPAIRSWAP_SECONDARY;
|
||||
|
||||
prim_priv = primary->private;
|
||||
sec_priv = secondary->private;
|
||||
|
||||
/*
|
||||
* usually the device should be quiesced for swap
|
||||
* for paranoia stop device and requeue requests again
|
||||
@ -6182,6 +6187,25 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
|
||||
/* swap blocklayer device link */
|
||||
gdp = block->gdp;
|
||||
dasd_add_link_to_gendisk(gdp, secondary);
|
||||
rc = device_move(disk_to_dev(gdp), &secondary->cdev->dev, DPM_ORDER_NONE);
|
||||
if (rc) {
|
||||
dev_err(&primary->cdev->dev,
|
||||
"copy_pair_swap: moving blockdevice parent %s->%s failed (%d)\n",
|
||||
dev_name(&primary->cdev->dev),
|
||||
dev_name(&secondary->cdev->dev), rc);
|
||||
}
|
||||
|
||||
if (primary->stopped & DASD_STOPPED_QUIESCE) {
|
||||
dasd_device_set_stop_bits(secondary, DASD_STOPPED_QUIESCE);
|
||||
dasd_device_remove_stop_bits(primary, DASD_STOPPED_QUIESCE);
|
||||
}
|
||||
|
||||
/*
|
||||
* The secondary device never got through format detection, but since it
|
||||
* is a copy of the primary device, the format is exactly the same;
|
||||
* therefore, the detected layout can simply be copied.
|
||||
*/
|
||||
sec_priv->uses_cdl = prim_priv->uses_cdl;
|
||||
|
||||
/* re-enable device */
|
||||
dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC);
|
||||
|
||||
@ -857,8 +857,11 @@ void iscsit_dec_conn_usage_count(struct iscsit_conn *conn)
|
||||
spin_lock_bh(&conn->conn_usage_lock);
|
||||
conn->conn_usage_count--;
|
||||
|
||||
if (!conn->conn_usage_count && conn->conn_waiting_on_uc)
|
||||
if (!conn->conn_usage_count && conn->conn_waiting_on_uc) {
|
||||
spin_unlock_bh(&conn->conn_usage_lock);
|
||||
complete(&conn->conn_waiting_on_uc_comp);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&conn->conn_usage_lock);
|
||||
}
|
||||
|
||||
@ -364,6 +364,8 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
|
||||
ret = sprintf(buffer, "<denied>");
|
||||
return ret;
|
||||
}
|
||||
if (ret > PAGE_SIZE)
|
||||
return -ENOSPC;
|
||||
|
||||
buildid = kmalloc(sizeof(*buildid) + ret, GFP_KERNEL);
|
||||
if (!buildid)
|
||||
@ -371,8 +373,10 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
|
||||
|
||||
buildid->len = ret;
|
||||
ret = HYPERVISOR_xen_version(XENVER_build_id, buildid);
|
||||
if (ret > 0)
|
||||
ret = sprintf(buffer, "%s", buildid->buf);
|
||||
if (ret > 0) {
|
||||
/* Build id is binary, not a string. */
|
||||
memcpy(buffer, buildid->buf, ret);
|
||||
}
|
||||
kfree(buildid);
|
||||
|
||||
return ret;
|
||||
|
||||
@ -442,13 +442,16 @@ static int rsb_cmp(struct dlm_rsb *r, const char *name, int nlen)
|
||||
return memcmp(r->res_name, maxname, DLM_RESNAME_MAXLEN);
|
||||
}
|
||||
|
||||
int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
|
||||
struct dlm_rsb **r_ret)
|
||||
int dlm_search_rsb_tree(struct rb_root *tree, char *name,
|
||||
unsigned int len, struct dlm_rsb **r_ret)
|
||||
{
|
||||
struct rb_node *node = tree->rb_node;
|
||||
struct dlm_rsb *r;
|
||||
int rc;
|
||||
|
||||
if (len > DLM_RESNAME_MAXLEN)
|
||||
return -EINVAL;
|
||||
|
||||
while (node) {
|
||||
r = rb_entry(node, struct dlm_rsb, res_hashnode);
|
||||
rc = rsb_cmp(r, name, len);
|
||||
|
||||
@ -30,8 +30,8 @@ void dlm_adjust_timeouts(struct dlm_ls *ls);
|
||||
int dlm_master_lookup(struct dlm_ls *ls, int nodeid, char *name, int len,
|
||||
unsigned int flags, int *r_nodeid, int *result);
|
||||
|
||||
int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
|
||||
struct dlm_rsb **r_ret);
|
||||
int dlm_search_rsb_tree(struct rb_root *tree, char *name,
|
||||
unsigned int len, struct dlm_rsb **r_ret);
|
||||
|
||||
void dlm_recover_purge(struct dlm_ls *ls);
|
||||
void dlm_purge_mstcpy_locks(struct dlm_rsb *r);
|
||||
|
||||
@ -27,10 +27,11 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
{
|
||||
struct smb2_err_rsp *err = iov->iov_base;
|
||||
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
|
||||
u8 *end = (u8 *)err + iov->iov_len;
|
||||
u32 len;
|
||||
|
||||
if (err->ErrorContextCount) {
|
||||
struct smb2_error_context_rsp *p, *end;
|
||||
struct smb2_error_context_rsp *p;
|
||||
|
||||
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
|
||||
ErrorContextData) +
|
||||
@ -39,8 +40,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
p = (struct smb2_error_context_rsp *)err->ErrorData;
|
||||
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
|
||||
do {
|
||||
while ((u8 *)p + sizeof(*p) <= end) {
|
||||
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
|
||||
sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
|
||||
break;
|
||||
@ -50,14 +50,16 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
|
||||
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
|
||||
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
|
||||
} while (p < end);
|
||||
}
|
||||
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
|
||||
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
|
||||
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
|
||||
}
|
||||
|
||||
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
||||
if (!IS_ERR(sym) &&
|
||||
((u8 *)sym + sizeof(*sym) > end ||
|
||||
le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
||||
sym = ERR_PTR(-EINVAL);
|
||||
|
||||
return sym;
|
||||
@ -128,8 +130,10 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
|
||||
print_len = le16_to_cpu(sym->PrintNameLength);
|
||||
print_offs = le16_to_cpu(sym->PrintNameOffset);
|
||||
|
||||
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
|
||||
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
|
||||
if ((char *)sym->PathBuffer + sub_offs + sub_len >
|
||||
(char *)iov->iov_base + iov->iov_len ||
|
||||
(char *)sym->PathBuffer + print_offs + print_len >
|
||||
(char *)iov->iov_base + iov->iov_len)
|
||||
return -EINVAL;
|
||||
|
||||
return smb2_parse_native_symlink(path,
|
||||
|
||||
@ -55,8 +55,12 @@ struct dpll_device_ops {
|
||||
void *dpll_priv, u32 *factor,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
RH_KABI_RESERVE(1)
|
||||
RH_KABI_RESERVE(2)
|
||||
RH_KABI_USE(1, int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state state,
|
||||
struct netlink_ext_ack *extack))
|
||||
RH_KABI_USE(2, int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state *state,
|
||||
struct netlink_ext_ack *extack))
|
||||
RH_KABI_RESERVE(3)
|
||||
RH_KABI_RESERVE(4)
|
||||
RH_KABI_RESERVE(5)
|
||||
@ -141,7 +145,10 @@ struct dpll_pin_ops {
|
||||
enum dpll_pin_state *state,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
RH_KABI_RESERVE(1)
|
||||
RH_KABI_USE(1, int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, u64 *measured_freq,
|
||||
struct netlink_ext_ack *extack))
|
||||
RH_KABI_RESERVE(2)
|
||||
RH_KABI_RESERVE(3)
|
||||
RH_KABI_RESERVE(4)
|
||||
|
||||
@ -348,9 +348,15 @@ extern gfp_t gfp_allowed_mask;
|
||||
/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */
|
||||
bool gfp_pfmemalloc_allowed(gfp_t gfp_mask);
|
||||
|
||||
/* A helper for checking if gfp includes all the specified flags */
|
||||
static inline bool gfp_has_flags(gfp_t gfp, gfp_t flags)
|
||||
{
|
||||
return (gfp & flags) == flags;
|
||||
}
|
||||
|
||||
static inline bool gfp_has_io_fs(gfp_t gfp)
|
||||
{
|
||||
return (gfp & (__GFP_IO | __GFP_FS)) == (__GFP_IO | __GFP_FS);
|
||||
return gfp_has_flags(gfp, __GFP_IO | __GFP_FS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -168,7 +168,8 @@ typedef unsigned int __bitwise gfp_t;
|
||||
* the caller still has to check for failures) while costly requests try to be
|
||||
* not disruptive and back off even without invoking the OOM killer.
|
||||
* The following three modifiers might be used to override some of these
|
||||
* implicit rules
|
||||
* implicit rules. Please note that all of them must be used along with
|
||||
* %__GFP_DIRECT_RECLAIM flag.
|
||||
*
|
||||
* %__GFP_NORETRY: The VM implementation will try only very lightweight
|
||||
* memory direct reclaim to get some memory under memory pressure (thus
|
||||
@ -199,11 +200,14 @@ typedef unsigned int __bitwise gfp_t;
|
||||
* cannot handle allocation failures. The allocation could block
|
||||
* indefinitely but will never return with failure. Testing for
|
||||
* failure is pointless.
|
||||
* It _must_ be blockable and used together with __GFP_DIRECT_RECLAIM.
|
||||
* It should _never_ be used in non-sleepable contexts.
|
||||
* New users should be evaluated carefully (and the flag should be
|
||||
* used only when there is no reasonable failure policy) but it is
|
||||
* definitely preferable to use the flag rather than opencode endless
|
||||
* loop around allocator.
|
||||
* Using this flag for costly allocations is _highly_ discouraged.
|
||||
* Allocating pages from the buddy with __GFP_NOFAIL and order > 1 is
|
||||
* not supported. Please consider using kvmalloc() instead.
|
||||
*/
|
||||
#define __GFP_IO ((__force gfp_t)___GFP_IO)
|
||||
#define __GFP_FS ((__force gfp_t)___GFP_FS)
|
||||
|
||||
@ -84,6 +84,11 @@ void nf_conntrack_lock(spinlock_t *lock);
|
||||
|
||||
extern spinlock_t nf_conntrack_expect_lock;
|
||||
|
||||
static inline void lockdep_nfct_expect_lock_held(void)
|
||||
{
|
||||
lockdep_assert_held(&nf_conntrack_expect_lock);
|
||||
}
|
||||
|
||||
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
|
||||
|
||||
#if (IS_BUILTIN(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \
|
||||
|
||||
@ -190,7 +190,8 @@ enum dpll_pin_capabilities {
|
||||
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
|
||||
};
|
||||
|
||||
#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000
|
||||
|
||||
/**
|
||||
* enum dpll_feature_state - Allow control (enable/disable) and status checking
|
||||
@ -217,6 +218,7 @@ enum dpll_a {
|
||||
DPLL_A_CLOCK_QUALITY_LEVEL,
|
||||
DPLL_A_PHASE_OFFSET_MONITOR,
|
||||
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
DPLL_A_FREQUENCY_MONITOR,
|
||||
|
||||
__DPLL_A_MAX,
|
||||
DPLL_A_MAX = (__DPLL_A_MAX - 1)
|
||||
@ -253,6 +255,7 @@ enum dpll_a_pin {
|
||||
DPLL_A_PIN_REFERENCE_SYNC,
|
||||
DPLL_A_PIN_PHASE_ADJUST_GRAN,
|
||||
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
|
||||
__DPLL_A_PIN_MAX,
|
||||
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.12.1.el9.x86_64,mailto:secalert@redhat.com
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.12.1.el9.x86_64,mailto:security@almalinux.org
|
||||
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.15.1.el9.x86_64,mailto:secalert@redhat.com
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.15.1.el9.x86_64,mailto:security@almalinux.org
|
||||
|
||||
219
mm/page_alloc.c
219
mm/page_alloc.c
@ -290,6 +290,14 @@ int user_min_free_kbytes = -1;
|
||||
static int watermark_boost_factor __read_mostly = 15000;
|
||||
static int watermark_scale_factor = 10;
|
||||
|
||||
/*
|
||||
* RHEL-ONLY: When set to 1, allows reclaim for __GFP_THISNODE THP allocations,
|
||||
* restoring the behavior prior to the fix that prevents zone_reclaim_mode-like
|
||||
* excessive reclaim on a single NUMA node when other nodes have plenty of free
|
||||
* memory.
|
||||
*/
|
||||
static int thp_thisnode_reclaim __read_mostly = 1;
|
||||
|
||||
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
|
||||
int movable_zone;
|
||||
EXPORT_SYMBOL(movable_zone);
|
||||
@ -2915,12 +2923,6 @@ struct page *rmqueue(struct zone *preferred_zone,
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
/*
|
||||
* We most definitely don't want callers attempting to
|
||||
* allocate greater than order-1 page units with __GFP_NOFAIL.
|
||||
*/
|
||||
WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
|
||||
|
||||
if (likely(pcp_allowed_order(order))) {
|
||||
page = rmqueue_pcplist(preferred_zone, zone, order,
|
||||
migratetype, alloc_flags);
|
||||
@ -4062,7 +4064,8 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
|
||||
struct alloc_context *ac)
|
||||
{
|
||||
bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;
|
||||
bool can_compact = gfp_compaction_allowed(gfp_mask);
|
||||
bool can_compact = can_direct_reclaim && gfp_compaction_allowed(gfp_mask);
|
||||
bool nofail = gfp_mask & __GFP_NOFAIL;
|
||||
const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER;
|
||||
struct page *page = NULL;
|
||||
unsigned int alloc_flags;
|
||||
@ -4074,6 +4077,27 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
|
||||
unsigned int cpuset_mems_cookie;
|
||||
unsigned int zonelist_iter_cookie;
|
||||
int reserve_flags;
|
||||
bool compact_first = false;
|
||||
bool can_retry_reserves = true;
|
||||
|
||||
if (unlikely(nofail)) {
|
||||
/*
|
||||
* We most definitely don't want callers attempting to
|
||||
* allocate greater than order-1 page units with __GFP_NOFAIL.
|
||||
*/
|
||||
WARN_ON_ONCE(order > 1);
|
||||
/*
|
||||
* Also we don't support __GFP_NOFAIL without __GFP_DIRECT_RECLAIM,
|
||||
* otherwise, we may result in lockup.
|
||||
*/
|
||||
WARN_ON_ONCE(!can_direct_reclaim);
|
||||
/*
|
||||
* PF_MEMALLOC request from this context is rather bizarre
|
||||
* because we cannot reclaim anything and only can loop waiting
|
||||
* for somebody to do a work for us.
|
||||
*/
|
||||
WARN_ON_ONCE(current->flags & PF_MEMALLOC);
|
||||
}
|
||||
|
||||
restart:
|
||||
compaction_retries = 0;
|
||||
@ -4082,6 +4106,19 @@ restart:
|
||||
cpuset_mems_cookie = read_mems_allowed_begin();
|
||||
zonelist_iter_cookie = zonelist_iter_begin();
|
||||
|
||||
/*
|
||||
* For costly allocations, try direct compaction first, as it's likely
|
||||
* that we have enough base pages and don't need to reclaim. For non-
|
||||
* movable high-order allocations, do that as well, as compaction will
|
||||
* try prevent permanent fragmentation by migrating from blocks of the
|
||||
* same migratetype.
|
||||
*/
|
||||
if (can_compact && (costly_order || (order > 0 &&
|
||||
ac->migratetype != MIGRATE_MOVABLE))) {
|
||||
compact_first = true;
|
||||
compact_priority = INIT_COMPACT_PRIORITY;
|
||||
}
|
||||
|
||||
/*
|
||||
* The fast path uses conservative alloc_flags to succeed only until
|
||||
* kswapd needs to be woken up, and to avoid the cost of setting up
|
||||
@ -4113,6 +4150,8 @@ restart:
|
||||
goto nopage;
|
||||
}
|
||||
|
||||
retry:
|
||||
/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */
|
||||
if (alloc_flags & ALLOC_KSWAPD)
|
||||
wake_all_kswapds(order, gfp_mask, ac);
|
||||
|
||||
@ -4124,66 +4163,6 @@ restart:
|
||||
if (page)
|
||||
goto got_pg;
|
||||
|
||||
/*
|
||||
* For costly allocations, try direct compaction first, as it's likely
|
||||
* that we have enough base pages and don't need to reclaim. For non-
|
||||
* movable high-order allocations, do that as well, as compaction will
|
||||
* try prevent permanent fragmentation by migrating from blocks of the
|
||||
* same migratetype.
|
||||
* Don't try this for allocations that are allowed to ignore
|
||||
* watermarks, as the ALLOC_NO_WATERMARKS attempt didn't yet happen.
|
||||
*/
|
||||
if (can_direct_reclaim && can_compact &&
|
||||
(costly_order ||
|
||||
(order > 0 && ac->migratetype != MIGRATE_MOVABLE))
|
||||
&& !gfp_pfmemalloc_allowed(gfp_mask)) {
|
||||
page = __alloc_pages_direct_compact(gfp_mask, order,
|
||||
alloc_flags, ac,
|
||||
INIT_COMPACT_PRIORITY,
|
||||
&compact_result);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
|
||||
/*
|
||||
* Checks for costly allocations with __GFP_NORETRY, which
|
||||
* includes some THP page fault allocations
|
||||
*/
|
||||
if (costly_order && (gfp_mask & __GFP_NORETRY)) {
|
||||
/*
|
||||
* If allocating entire pageblock(s) and compaction
|
||||
* failed because all zones are below low watermarks
|
||||
* or is prohibited because it recently failed at this
|
||||
* order, fail immediately unless the allocator has
|
||||
* requested compaction and reclaim retry.
|
||||
*
|
||||
* Reclaim is
|
||||
* - potentially very expensive because zones are far
|
||||
* below their low watermarks or this is part of very
|
||||
* bursty high order allocations,
|
||||
* - not guaranteed to help because isolate_freepages()
|
||||
* may not iterate over freed pages as part of its
|
||||
* linear scan, and
|
||||
* - unlikely to make entire pageblocks free on its
|
||||
* own.
|
||||
*/
|
||||
if (compact_result == COMPACT_SKIPPED ||
|
||||
compact_result == COMPACT_DEFERRED)
|
||||
goto nopage;
|
||||
|
||||
/*
|
||||
* Looks like reclaim/compaction is worth trying, but
|
||||
* sync compaction could be very expensive, so keep
|
||||
* using async compaction.
|
||||
*/
|
||||
compact_priority = INIT_COMPACT_PRIORITY;
|
||||
}
|
||||
}
|
||||
|
||||
retry:
|
||||
/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */
|
||||
if (alloc_flags & ALLOC_KSWAPD)
|
||||
wake_all_kswapds(order, gfp_mask, ac);
|
||||
|
||||
reserve_flags = __gfp_pfmemalloc_flags(gfp_mask);
|
||||
if (reserve_flags)
|
||||
alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, reserve_flags) |
|
||||
@ -4198,12 +4177,18 @@ retry:
|
||||
ac->nodemask = NULL;
|
||||
ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,
|
||||
ac->highest_zoneidx, ac->nodemask);
|
||||
}
|
||||
|
||||
/* Attempt with potentially adjusted zonelist and alloc_flags */
|
||||
page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
/*
|
||||
* The first time we adjust anything due to being allowed to
|
||||
* ignore memory policies or watermarks, retry immediately. This
|
||||
* allows us to keep the first allocation attempt optimistic so
|
||||
* it can succeed in a zone that is still above watermarks.
|
||||
*/
|
||||
if (can_retry_reserves) {
|
||||
can_retry_reserves = false;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Caller is not willing to reclaim, we can't balance anything */
|
||||
if (!can_direct_reclaim)
|
||||
@ -4214,10 +4199,12 @@ retry:
|
||||
goto nopage;
|
||||
|
||||
/* Try direct reclaim and then allocating */
|
||||
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,
|
||||
&did_some_progress);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
if (!compact_first) {
|
||||
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags,
|
||||
ac, &did_some_progress);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
}
|
||||
|
||||
/* Try direct compaction and then allocating */
|
||||
page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
|
||||
@ -4225,6 +4212,44 @@ retry:
|
||||
if (page)
|
||||
goto got_pg;
|
||||
|
||||
if (compact_first) {
|
||||
/*
|
||||
* THP page faults may attempt local node only first, but are
|
||||
* then allowed to only compact, not reclaim, see
|
||||
* alloc_pages_mpol().
|
||||
*
|
||||
* Compaction has failed above and we don't want such THP
|
||||
* allocations to put reclaim pressure on a single node in a
|
||||
* situation where other nodes might have plenty of available
|
||||
* memory.
|
||||
*
|
||||
* RHEL-ONLY: vm.thp_thisnode_reclaim can override this to
|
||||
* restore the pre-fix behavior: allow reclaim for THISNODE THP
|
||||
* allocations, but still fail immediately when compaction was
|
||||
* skipped (insufficient free base pages) or deferred (recent
|
||||
* compaction failures at this order).
|
||||
*/
|
||||
if (gfp_has_flags(gfp_mask, __GFP_NORETRY | __GFP_THISNODE)) {
|
||||
if (!thp_thisnode_reclaim)
|
||||
goto nopage;
|
||||
if (compact_result == COMPACT_SKIPPED ||
|
||||
compact_result == COMPACT_DEFERRED)
|
||||
goto nopage;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the initial compaction attempt we have lowered its
|
||||
* priority. Restore it for further retries, if those are
|
||||
* allowed. With __GFP_NORETRY there will be a single round of
|
||||
* reclaim and compaction with the lowered priority.
|
||||
*/
|
||||
if (!(gfp_mask & __GFP_NORETRY))
|
||||
compact_priority = DEF_COMPACT_PRIORITY;
|
||||
|
||||
compact_first = false;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Do not loop if specifically requested */
|
||||
if (gfp_mask & __GFP_NORETRY)
|
||||
goto nopage;
|
||||
@ -4237,6 +4262,15 @@ retry:
|
||||
!(gfp_mask & __GFP_RETRY_MAYFAIL)))
|
||||
goto nopage;
|
||||
|
||||
/*
|
||||
* Deal with possible cpuset update races or zonelist updates to avoid
|
||||
* infinite retries. No "goto retry;" can be placed above this check
|
||||
* unless it can execute just once.
|
||||
*/
|
||||
if (check_retry_cpuset(cpuset_mems_cookie, ac) ||
|
||||
check_retry_zonelist(zonelist_iter_cookie))
|
||||
goto restart;
|
||||
|
||||
if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
|
||||
did_some_progress > 0, &no_progress_loops))
|
||||
goto retry;
|
||||
@ -4292,29 +4326,15 @@ nopage:
|
||||
* Make sure that __GFP_NOFAIL request doesn't leak out and make sure
|
||||
* we always retry
|
||||
*/
|
||||
if (gfp_mask & __GFP_NOFAIL) {
|
||||
if (unlikely(nofail)) {
|
||||
/*
|
||||
* All existing users of the __GFP_NOFAIL are blockable, so warn
|
||||
* of any new users that actually require GFP_NOWAIT
|
||||
* Lacking direct_reclaim we can't do anything to reclaim memory,
|
||||
* we disregard these unreasonable nofail requests and still
|
||||
* return NULL
|
||||
*/
|
||||
if (WARN_ON_ONCE_GFP(!can_direct_reclaim, gfp_mask))
|
||||
if (!can_direct_reclaim)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* PF_MEMALLOC request from this context is rather bizarre
|
||||
* because we cannot reclaim anything and only can loop waiting
|
||||
* for somebody to do a work for us
|
||||
*/
|
||||
WARN_ON_ONCE_GFP(current->flags & PF_MEMALLOC, gfp_mask);
|
||||
|
||||
/*
|
||||
* non failing costly orders are a hard requirement which we
|
||||
* are not prepared for much so let's warn about these users
|
||||
* so that we can identify them and convert them to something
|
||||
* else.
|
||||
*/
|
||||
WARN_ON_ONCE_GFP(costly_order, gfp_mask);
|
||||
|
||||
/*
|
||||
* Help non-failing allocations by giving some access to memory
|
||||
* reserves normally used for high priority non-blocking
|
||||
@ -6213,6 +6233,15 @@ static struct ctl_table page_alloc_sysctl_table[] = {
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE_HUNDRED,
|
||||
},
|
||||
{
|
||||
.procname = "thp_thisnode_reclaim", //RHEL-ONLY
|
||||
.data = &thp_thisnode_reclaim,
|
||||
.maxlen = sizeof(thp_thisnode_reclaim),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
},
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
|
||||
@ -479,6 +479,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct inet6_dev *idev;
|
||||
struct ipv6hdr *oldhdr;
|
||||
unsigned int chdr_len;
|
||||
struct in6_addr addr;
|
||||
unsigned char *buf;
|
||||
int accept_rpl_seg;
|
||||
@ -601,8 +602,10 @@ looped_back:
|
||||
skb_pull(skb, ((hdr->hdrlen + 1) << 3));
|
||||
skb_postpull_rcsum(skb, oldhdr,
|
||||
sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3));
|
||||
if (unlikely(!hdr->segments_left)) {
|
||||
if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
|
||||
chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3);
|
||||
if (unlikely(!hdr->segments_left ||
|
||||
skb_headroom(skb) < chdr_len + skb->mac_len)) {
|
||||
if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0,
|
||||
GFP_ATOMIC)) {
|
||||
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS);
|
||||
kfree_skb(skb);
|
||||
@ -612,7 +615,7 @@ looped_back:
|
||||
|
||||
oldhdr = ipv6_hdr(skb);
|
||||
}
|
||||
skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr));
|
||||
skb_push(skb, chdr_len);
|
||||
skb_reset_network_header(skb);
|
||||
skb_mac_header_rebuild(skb);
|
||||
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
||||
|
||||
@ -603,11 +603,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
if (!skb2)
|
||||
return 0;
|
||||
|
||||
/* Remove debris left by IPv6 stack. */
|
||||
memset(IPCB(skb2), 0, sizeof(*IPCB(skb2)));
|
||||
|
||||
skb_dst_drop(skb2);
|
||||
|
||||
skb_pull(skb2, offset);
|
||||
skb_reset_network_header(skb2);
|
||||
eiph = ip_hdr(skb2);
|
||||
if (eiph->version != 4 || eiph->ihl < 5)
|
||||
goto out;
|
||||
|
||||
/* Try to guess incoming interface */
|
||||
rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr,
|
||||
|
||||
@ -237,6 +237,8 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
|
||||
struct nf_ct_event_notifier *notify;
|
||||
struct nf_conntrack_ecache *e;
|
||||
|
||||
lockdep_nfct_expect_lock_held();
|
||||
|
||||
rcu_read_lock();
|
||||
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
|
||||
if (!notify)
|
||||
|
||||
@ -51,6 +51,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
|
||||
struct net *net = nf_ct_exp_net(exp);
|
||||
struct nf_conntrack_net *cnet;
|
||||
|
||||
lockdep_nfct_expect_lock_held();
|
||||
WARN_ON(!master_help);
|
||||
WARN_ON(timer_pending(&exp->timeout));
|
||||
|
||||
@ -118,6 +119,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
|
||||
|
||||
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
lockdep_nfct_expect_lock_held();
|
||||
|
||||
if (del_timer(&exp->timeout)) {
|
||||
nf_ct_unlink_expect(exp);
|
||||
nf_ct_expect_put(exp);
|
||||
@ -177,6 +180,8 @@ nf_ct_find_expectation(struct net *net,
|
||||
struct nf_conntrack_expect *i, *exp = NULL;
|
||||
unsigned int h;
|
||||
|
||||
lockdep_nfct_expect_lock_held();
|
||||
|
||||
if (!cnet->expect_count)
|
||||
return NULL;
|
||||
|
||||
@ -442,6 +447,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
|
||||
unsigned int h;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_nfct_expect_lock_held();
|
||||
|
||||
if (!master_help) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
@ -498,8 +505,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
|
||||
|
||||
nf_ct_expect_insert(expect);
|
||||
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
|
||||
@ -3329,31 +3329,37 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb2)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&nf_conntrack_expect_lock);
|
||||
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
|
||||
if (!exp)
|
||||
if (!exp) {
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
kfree_skb(skb2);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (cda[CTA_EXPECT_ID]) {
|
||||
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
|
||||
|
||||
if (id != nf_expect_get_id(exp)) {
|
||||
nf_ct_expect_put(exp);
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
kfree_skb(skb2);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb2) {
|
||||
nf_ct_expect_put(exp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
|
||||
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
|
||||
exp);
|
||||
rcu_read_unlock();
|
||||
nf_ct_expect_put(exp);
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
|
||||
if (err <= 0) {
|
||||
kfree_skb(skb2);
|
||||
return -ENOMEM;
|
||||
@ -3403,22 +3409,26 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock_bh(&nf_conntrack_expect_lock);
|
||||
|
||||
/* bump usage count to 2 */
|
||||
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
|
||||
if (!exp)
|
||||
if (!exp) {
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (cda[CTA_EXPECT_ID]) {
|
||||
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
|
||||
|
||||
if (id != nf_expect_get_id(exp)) {
|
||||
nf_ct_expect_put(exp);
|
||||
spin_unlock_bh(&nf_conntrack_expect_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* after list removal, usage count == 1 */
|
||||
spin_lock_bh(&nf_conntrack_expect_lock);
|
||||
if (del_timer(&exp->timeout)) {
|
||||
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
|
||||
nlmsg_report(info->nlh));
|
||||
|
||||
@ -144,11 +144,15 @@ static void vport_netdev_free(struct rcu_head *rcu)
|
||||
void ovs_netdev_detach_dev(struct vport *vport)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
|
||||
netdev_rx_handler_unregister(vport->dev);
|
||||
netdev_upper_dev_unlink(vport->dev,
|
||||
netdev_master_upper_dev_get(vport->dev));
|
||||
dev_set_promiscuity(vport->dev, -1);
|
||||
|
||||
/* paired with smp_mb() in netdev_destroy() */
|
||||
smp_wmb();
|
||||
|
||||
vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
|
||||
}
|
||||
|
||||
static void netdev_destroy(struct vport *vport)
|
||||
@ -167,6 +171,9 @@ static void netdev_destroy(struct vport *vport)
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
/* paired with smp_wmb() in ovs_netdev_detach_dev() */
|
||||
smp_mb();
|
||||
|
||||
call_rcu(&vport->rcu, vport_netdev_free);
|
||||
}
|
||||
|
||||
|
||||
@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
|
||||
usb6fire_comm_abort(chip);
|
||||
if (chip->control)
|
||||
usb6fire_control_abort(chip);
|
||||
if (chip->card) {
|
||||
snd_card_disconnect(chip->card);
|
||||
snd_card_free_when_closed(chip->card);
|
||||
chip->card = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,6 +163,7 @@ destroy_chip:
|
||||
static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct sfire_chip *chip;
|
||||
struct snd_card *card;
|
||||
|
||||
chip = usb_get_intfdata(intf);
|
||||
if (chip) { /* if !chip, fw upload has been performed */
|
||||
@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
chips[chip->regidx] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save card pointer before teardown.
|
||||
* snd_card_free_when_closed() may free card (and
|
||||
* the embedded chip) immediately, so it must be
|
||||
* called last and chip must not be accessed after.
|
||||
*/
|
||||
card = chip->card;
|
||||
chip->shutdown = true;
|
||||
if (card)
|
||||
snd_card_disconnect(card);
|
||||
usb6fire_chip_abort(chip);
|
||||
if (card)
|
||||
snd_card_free_when_closed(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user