From ee15d542594ab4f38e0430bd8e6a3fefe81095f6 Mon Sep 17 00:00:00 2001 From: Milena Olech Date: Thu, 29 Sep 2022 02:34:02 +0200 Subject: [PATCH 1/6] dpll: add ice implementation for pin object and muxed pins Design has been changed by separating pins (sources/outputs) from dpll. This patch introduces adjustments in ice driver. Co-developed-by: Arkadiusz Kubalewski Signed-off-by: Arkadiusz Kubalewski Signed-off-by: Milena Olech --- drivers/net/ethernet/intel/ice/ice_synce.c | 248 +++++++++++++++------ drivers/net/ethernet/intel/ice/ice_synce.h | 1 + 2 files changed, 187 insertions(+), 62 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index 9b9820fc388f4..4befbefe95325 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -102,13 +102,19 @@ static int ice_synce_get_lock_status(struct dpll_device *dpll) * Check current type of a given pin. * Return: current type value of a pin. */ -static int ice_synce_get_source_type(struct dpll_device *dpll, int id) +static int ice_synce_get_source_type(struct dpll_pin *pin) { - struct ice_pf *pf = dpll_priv(dpll); + struct ice_pf *pf = pin_priv(pin); int ret; + u8 id; + + if (!pf) + return DPLL_TYPE_NONE; + id = (u8)pin_id(pin); if (id < 0 || id >= pf->synce.num_inputs) return DPLL_TYPE_NONE; + mutex_lock(&pf->synce.lock); ret = pf->synce.inputs[id].type; mutex_unlock(&pf->synce.lock); @@ -127,13 +133,22 @@ static int ice_synce_get_source_type(struct dpll_device *dpll, int id) * * 0 - success, * * negative - failure. */ -static int ice_synce_set_source_type(struct dpll_device *dpll, int id, int type) +static int ice_synce_set_source_type(struct dpll_pin *pin, int type) { - struct ice_pf *pf = dpll_priv(dpll); - struct ice_synce *se = &pf->synce; + struct ice_pf *pf = pin_priv(pin); struct ice_synce_pin *inputs; + struct ice_synce *se; int ret; + u8 id; + if (!pf) + return -ENODEV; + + se = &pf->synce; + if (!se) + return -ENOSPC; + + id = (u8)pin_id(pin); if (id < 0 || id >= se->num_inputs) return -EINVAL; inputs = se->inputs; @@ -171,14 +186,22 @@ static int ice_synce_set_source_type(struct dpll_device *dpll, int id, int type) * dpll subsystem callback. * Check if given type is supported on a given pin. * Return: - * * true - if supported - * * false - if not supported + * * value - if supported + * * 0 - if not supported */ static int -ice_synce_get_source_supported(struct dpll_device *dpll, int id, int type) +ice_synce_get_source_supported(struct dpll_pin *pin, int type) { - struct ice_pf *pf = dpll_priv(dpll); + struct ice_pf *pf = pin_priv(pin); int ret; + u8 id; + + if (!pf) + return 0; + + id = (u8)pin_id(pin); + if (id >= pf->synce.num_outputs) + return 0; mutex_lock(&pf->synce.lock); ret = !!(pf->synce.inputs[id].types_supported & BIT(type)); @@ -196,13 +219,19 @@ ice_synce_get_source_supported(struct dpll_device *dpll, int id, int type) * Check current type of a given pin. * Return: current type value of a pin. */ -static int ice_synce_get_output_type(struct dpll_device *dpll, int id) +static int ice_synce_get_output_type(struct dpll_pin *pin) { - struct ice_pf *pf = dpll_priv(dpll); + struct ice_pf *pf = pin_priv(pin); int ret; + u8 id; + if (!pf) + return DPLL_TYPE_NONE; + + id = (u8)pin_id(pin); if (id < 0 || id >= pf->synce.num_outputs) return DPLL_TYPE_NONE; + mutex_lock(&pf->synce.lock); ret = pf->synce.outputs[id].type; mutex_unlock(&pf->synce.lock); @@ -212,26 +241,35 @@ static int ice_synce_get_output_type(struct dpll_device *dpll, int id) /** * ice_synce_set_output_type - * @dpll: registered dpll pointer - * @id: output pin index + * @pin: registered pin pointer + * @type: type to set * - * dpll subsystem callback. + * pin subsystem callback. * Set type of a given output pin. * Return: * * 0 - success, * * negative - failure. */ -static int ice_synce_set_output_type(struct dpll_device *dpll, int id, int type) +static int ice_synce_set_output_type(struct dpll_pin *pin, int type) { - struct ice_pf *pf = dpll_priv(dpll); - struct ice_synce *se = &pf->synce; + struct ice_pf *pf = pin_priv(pin); struct ice_synce_pin *outputs; + struct ice_synce *se; int ret; + u8 id; + if (!pf) + return -ENODEV; + + se = &pf->synce; + if (!se) + return -ENOSPC; + + id = (u8)pin_id(pin); if (id < 0 || id >= se->num_outputs) return -EINVAL; - outputs = se->outputs; + outputs = se->outputs; if (type == outputs[id].type) return 0; @@ -258,21 +296,28 @@ static int ice_synce_set_output_type(struct dpll_device *dpll, int id, int type) /** * ice_synce_get_output_supported - * @dpll: registered dpll pointer - * @id: output pin index + * @pin: registered pin pointer * @type: id of type * - * dpll subsystem callback. + * pin subsystem callback. * Check if given type is supported on a given pin. * Return: - * * true - if supported - * * false - if not supported + * * value - if supported + * * 0 - if not supported */ static int -ice_synce_get_output_supported(struct dpll_device *dpll, int id, int type) +ice_synce_get_output_supported(struct dpll_pin *pin, int type) { - struct ice_pf *pf = dpll_priv(dpll); + struct ice_pf *pf = pin_priv(pin); int ret; + u8 id; + + if (!pf) + return 0; + + id = (u8)pin_id(pin); + if (id >= pf->synce.num_outputs) + return 0; mutex_lock(&pf->synce.lock); ret = !!(pf->synce.outputs[id].types_supported & BIT(type)); @@ -322,12 +367,15 @@ static int ice_synce_get_src_select_supported(struct dpll_device *dpll, int mode * Get source priority value. * Return: source priority value */ -static int ice_synce_get_source_prio(struct dpll_device *dpll, int id) +static int ice_synce_get_source_prio(struct dpll_pin *pin, struct dpll_device *dpll) { struct ice_pf *pf = dpll_priv(dpll); - u8 idx = (u8)id; + u8 idx = (u8)pin_id(pin); int ret; + if (!pf) + return -ENODEV; + if (idx >= pf->synce.num_inputs) return 0xFF; @@ -350,16 +398,22 @@ static int ice_synce_get_source_prio(struct dpll_device *dpll, int id) * * 0 - success * * negative - failure */ -static int ice_synce_set_source_prio(struct dpll_device *dpll, int id, int prio) +static int ice_synce_set_source_prio(struct dpll_pin *pin, + struct dpll_device *dpll, int prio) { struct ice_pf *pf = dpll_priv(dpll); - struct ice_synce *se = &pf->synce; - u8 idx = (u8)id, priov = (u8)prio; + u8 idx, priov = (u8)prio; + struct ice_synce *se; int ret; + if (!pf) + return -ENODEV; + se = &pf->synce; + if (priov > ICE_SYNCE_PRIO_MAX) return -EINVAL; + idx = (u8)pin_id(pin); if (idx >= pf->synce.num_inputs) return -EINVAL; @@ -377,43 +431,25 @@ static int ice_synce_set_source_prio(struct dpll_device *dpll, int id, int prio) return ret; } -const char *ice_synce_get_source_name(struct dpll_device *dpll, int id) -{ - struct ice_pf *pf = dpll_priv(dpll); - u8 idx = (u8)id; - - if (idx >= pf->synce.num_inputs) - return NULL; - - return pf->synce.inputs[idx].name; -} - -const char *ice_synce_get_output_name(struct dpll_device *dpll, int id) -{ - struct ice_pf *pf = dpll_priv(dpll); - u8 idx = (u8)id; - - if (idx >= pf->synce.num_outputs) - return NULL; +static struct dpll_pin_ops ice_synce_source_ops = { + .get_type = ice_synce_get_source_type, + .set_type = ice_synce_set_source_type, + .is_type_supported = ice_synce_get_source_supported, + .get_prio = ice_synce_get_source_prio, + .set_prio = ice_synce_set_source_prio, +}; - return pf->synce.outputs[idx].name; -} +static struct dpll_pin_ops ice_synce_output_ops = { + .get_type = ice_synce_get_output_type, + .set_type = ice_synce_set_output_type, + .is_type_supported = ice_synce_get_output_supported, +}; static struct dpll_device_ops ice_synce_dpll_ops = { .get_status = ice_synce_get_status, .get_lock_status = ice_synce_get_lock_status, - .get_source_type = ice_synce_get_source_type, - .set_source_type = ice_synce_set_source_type, - .get_source_supported = ice_synce_get_source_supported, - .get_output_type = ice_synce_get_output_type, - .set_output_type = ice_synce_set_output_type, - .get_output_supported = ice_synce_get_output_supported, .get_source_select_mode = ice_synce_get_source_select_mode, .get_source_select_mode_supported = ice_synce_get_src_select_supported, - .get_source_prio = ice_synce_get_source_prio, - .set_source_prio = ice_synce_set_source_prio, - .get_source_name = ice_synce_get_source_name, - .get_output_name = ice_synce_get_output_name, }; /** @@ -581,11 +617,88 @@ static int ice_synce_init_pins(struct ice_hw *hw, bool input, int num_pins, return ret; } +/** + * ice_synce_register_pins + * @dpll: dpll pointer + * @pins: pointer to pins array + * @count: no of pins + * + * Release dpll pins from dpll subsystem. + */ +static void ice_synce_release_pins(struct dpll_device *dpll, + struct ice_synce_pin *pins, int count) +{ + int i; + + for (i = 0; i < count; count++) { + if (pins[i].pin) { + dpll_pin_deregister(dpll, pins[i].pin); + dpll_pin_free(dpll, pins[i].pin); + pins[i].pin = NULL; + } + } +} + +/** + * ice_synce_register_pins + * @pf: Board private structure + * @dpll_device: registered dpll pointer + * @pins: pointer to the pins array + * @count: number of pins + * @inputs: pins type + * + * Register pins within a SyncE dpll in dpll subsystem. + * + * Return: + * * 0 - success + * * negative - error + */ +static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, + struct ice_synce_pin *pins, int count, + bool inputs) +{ + struct dpll_pin_ops *ops; + enum dpll_pin_type type; + int ret, i, alloc_size; + + if (inputs) { + type = DPLL_PIN_TYPE_SOURCE; + ops = &ice_synce_source_ops; + + } else { + type = DPLL_PIN_TYPE_OUTPUT; + ops = &ice_synce_output_ops; + } + + alloc_size = sizeof(dpll_pin) * count; + pins->pin = kmalloc(alloc_size, GFP_KERNEL); + + for (i = 0; i < count; i++) { + pins[i].pin.ops = ops; + pins[i].pin.id = id; + pins[i].pin.type = type; + pins[i].pin.priv = priv; + if (inputs) + pins[i].pin.name = "source"; + else + pins[i].pin.name = "output"; + + ret = dpll_pin_register(dpll, pins[i].pin); + if (ret) { + ice_synce_release_pins(dpll, pins, i + 1); + return ret; + } + } + + return 0; +} + /** * ice_synce_init_info * @pf: Board private structure * * Acquire (from HW) and set basic dpll information (on pf->synce struct). + * * Return: * * 0 - success * * negative - AQ error @@ -652,8 +765,7 @@ static int ice_synce_init_dpll(struct ice_pf *pf) snprintf(name, DPLL_NAME_LENGTH, "%s-SyncE-%s", dev_driver_string(dev), dev_name(dev)); - se->dpll = dpll_device_alloc(&ice_synce_dpll_ops, name, se->num_inputs, - se->num_outputs, pf); + se->dpll = dpll_device_alloc(&ice_synce_dpll_ops, name, pf); if (!se->dpll) { dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed\n"); return -ENOMEM; @@ -782,6 +894,18 @@ int ice_synce_init(struct ice_pf *pf) err = ice_synce_init_dpll(pf); if (err) goto free_info; + err = ice_synce_register_pins(pf, pf->synce.dpll, pf->synce.inputs, + pf->synce.num_inputs, true); + if (err) + goto free_info; + err = ice_synce_register_pins(pf, pf->synce.dpll, pf->synce.outputs, + pf->synce.num_outputs, false); + if (err) { + ice_synce_release_pins(pf->synce.dpll, pf->synce.inputs, + pf->synce.num_inputs); + goto free_info; + } + dev_dbg(ice_pf_to_dev(pf), "SyncE init successful\n"); mutex_unlock(&pf->synce.lock); diff --git a/drivers/net/ethernet/intel/ice/ice_synce.h b/drivers/net/ethernet/intel/ice/ice_synce.h index 0f2385cdfb3b5..514e218540cba 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.h +++ b/drivers/net/ethernet/intel/ice/ice_synce.h @@ -21,6 +21,7 @@ struct ice_synce_pin { u8 num_types; u8 flags; u8 prio; + struct dpll_pin *pin; const char *name; }; From 26433c90f931fdd78633e01ce912b25c95d7b6f0 Mon Sep 17 00:00:00 2001 From: Milena Olech Date: Wed, 28 Sep 2022 05:40:03 +0200 Subject: [PATCH 2/6] dpll: add pin object and muxed pins Change design by separating pins (sources/outputs) from dpll. Multiple dpll can share a pins, and pins can be either sources or outputs. The kernel module can also register a pin with different pin instead of dpll, this allows to have muxed pins attached from multiple driver instances within one already registered pin. Co-developed-by: Arkadiusz Kubalewski Signed-off-by: Arkadiusz Kubalewski Signed-off-by: Milena Olech --- drivers/dpll/dpll_core.c | 266 +++++++++++++++- drivers/dpll/dpll_core.h | 43 ++- drivers/dpll/dpll_netlink.c | 346 ++++++++++++++------- drivers/net/ethernet/intel/ice/ice_synce.c | 20 +- drivers/ptp/ptp_ocp.c | 2 +- include/linux/dpll.h | 200 +++++++++++- include/uapi/linux/dpll.h | 49 +-- 7 files changed, 760 insertions(+), 166 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 732165b77e384..0b5f499cfc4d5 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -15,6 +15,7 @@ #include "dpll_core.h" static DEFINE_MUTEX(dpll_device_xa_lock); + static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC); #define DPLL_REGISTERED XA_MARK_1 @@ -23,6 +24,7 @@ static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC); #define ASSERT_DPLL_NOT_REGISTERED(d) \ WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED)) +#define IS_TYPE_SOURCE(t) (t == DPLL_PIN_TYPE_SOURCE) int for_each_dpll_device(int id, int (*cb)(struct dpll_device *, void *), void *data) @@ -69,6 +71,7 @@ struct dpll_device *dpll_device_get_by_name(const char *name) return ret; } +EXPORT_SYMBOL_GPL(dpll_device_get_by_name); void *dpll_priv(struct dpll_device *dpll) { @@ -76,6 +79,18 @@ void *dpll_priv(struct dpll_device *dpll) } EXPORT_SYMBOL_GPL(dpll_priv); +void *pin_priv(struct dpll_pin *pin) +{ + return pin->priv; +} +EXPORT_SYMBOL_GPL(pin_priv); + +int pin_id(struct dpll_pin *pin) +{ + return pin->id; +} +EXPORT_SYMBOL_GPL(pin_id); + static void dpll_device_release(struct device *dev) { struct dpll_device *dpll; @@ -92,7 +107,7 @@ static struct class dpll_class = { }; struct dpll_device *dpll_device_alloc(struct dpll_device_ops *ops, const char *name, - int sources_count, int outputs_count, void *priv) + void *priv) { struct dpll_device *dpll; int ret; @@ -104,16 +119,19 @@ struct dpll_device *dpll_device_alloc(struct dpll_device_ops *ops, const char *n mutex_init(&dpll->lock); dpll->ops = ops; dpll->dev.class = &dpll_class; - dpll->sources_count = sources_count; - dpll->outputs_count = outputs_count; mutex_lock(&dpll_device_xa_lock); ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b, GFP_KERNEL); if (ret) goto error; - dev_set_name(&dpll->dev, "%s%d", name ? name : "dpll", dpll->id); + if (name) + dev_set_name(&dpll->dev, "%s", name); + else + dev_set_name(&dpll->dev, "%s%d", "dpll", dpll->id); + mutex_unlock(&dpll_device_xa_lock); dpll->priv = priv; + xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC); dpll_notify_device_create(dpll->id, dev_name(&dpll->dev)); @@ -131,6 +149,11 @@ void dpll_device_free(struct dpll_device *dpll) if (!dpll) return; +// if (!xa_empty(dpll->pins)) +// return; + + xa_destroy(&dpll->pins); + mutex_destroy(&dpll->lock); kfree(dpll); } @@ -157,6 +180,241 @@ void dpll_device_unregister(struct dpll_device *dpll) } EXPORT_SYMBOL_GPL(dpll_device_unregister); +void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, + struct dpll_pin_ops *ops, void *priv, const char *name, + int id) +{ + *pin = kmalloc(sizeof(**pin), GFP_KERNEL); + + (*pin)->ops = ops; + (*pin)->type = type; + (*pin)->priv = priv; + (*pin)->id = id; + if (name) + snprintf((*pin)->name, PIN_NAME_LENGTH, "%s", name); + else + snprintf((*pin)->name, PIN_NAME_LENGTH, "%s%d", + IS_TYPE_SOURCE((*pin)->type) ? "source" : "output", + (*pin)->id); +} +EXPORT_SYMBOL_GPL(dpll_init_pin); + +static int pin_register(struct xarray *pins, struct dpll_pin *pin) +{ + struct dpll_pin *pos; + unsigned long index; + int ret; + u32 id; + + xa_for_each(pins, index, pos) { + if (pos == pin) + return -EEXIST; + } + + ret = xa_alloc(pins, &id, pin, xa_limit_16b, GFP_KERNEL); + if (!ret) + xa_set_mark(pins, id, PIN_TYPE_TO_MARK(pin->type)); + + return ret; +} + +static void change_pin_count(struct dpll_device *dpll, struct dpll_pin *pin, + bool increment) +{ + if (IS_TYPE_SOURCE(pin->type)) { + if (increment) + dpll->sources_count++; + else + dpll->sources_count--; + } else { + if (increment) + dpll->outputs_count++; + else + dpll->outputs_count--; + } +} + +int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin) +{ + int ret; + + mutex_lock(&dpll->lock); + ret = pin_register(&dpll->pins, pin); + if (!ret) { + pin->ref_count++; + change_pin_count(dpll, pin, true); + xa_set_mark(&dpll->pins, dpll->id, DPLL_REGISTERED); + } + + mutex_unlock(&dpll->lock); + + if (!ret) + dpll_notify_pin_register(dpll->id, pin->id); + + return ret; + +} +EXPORT_SYMBOL_GPL(dpll_pin_register); + +static int pin_deregister(struct xarray *xa_pins, struct dpll_pin *pin) +{ + struct dpll_pin *pos; + unsigned long index; + + xa_for_each(xa_pins, index, pos) { + if (pos == pin) { + if (pin == xa_erase(xa_pins, index)) + return 0; + break; + } + } + + return -ENOENT; +} + +int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin) +{ + int ret = 0; + + if (xa_empty(&dpll->pins)) + return -ENOENT; + + mutex_lock(&dpll->lock); + ret = pin_deregister(&dpll->pins, pin); + if (!ret) + change_pin_count(dpll, pin, false); + + mutex_unlock(&dpll->lock); + + if (!ret) { + mutex_lock(&pin->lock); + pin->ref_count--; + mutex_unlock(&pin->lock); + dpll_notify_pin_deregister(dpll->id, pin->id); + } + + return ret; +} +EXPORT_SYMBOL_GPL(dpll_pin_deregister); + +void dpll_pin_free(struct dpll_device *dpll, struct dpll_pin *pin) +{ + struct dpll_pin *pos; + unsigned long index; + bool pin_found; + + xa_for_each(&dpll->pins, index, pos) { + if (pos == pin) + pin_found = true; + } + + if (!pin_found) + return; + + if (pin->ref_count) + return; + + xa_destroy(&pin->muxed_pins); + kfree(pin); +} +EXPORT_SYMBOL_GPL(dpll_pin_free); + +void dpll_muxed_pin_free(struct dpll_pin *parent_pin, struct dpll_pin *pin) +{ + struct dpll_pin *pos; + unsigned long index; + bool pin_found; + + xa_for_each(&parent_pin->muxed_pins, index, pos) { + if (pos == pin) + pin_found = true; + } + + if (!pin_found) + return; + + if (pin->ref_count) + return; + + xa_destroy(&pin->muxed_pins); + kfree(pin); +} +EXPORT_SYMBOL_GPL(dpll_muxed_pin_free); + +int dpll_muxed_pin_register(struct dpll_pin *parent_pin, struct dpll_pin *pin) +{ + int ret; + + ret = pin_register(&parent_pin->muxed_pins, pin); + + if (!ret) { + mutex_lock(&pin->lock); + pin->ref_count++; + mutex_unlock(&pin->lock); + dpll_notify_muxed_pin_register(parent_pin, pin->id); + } + + return ret; +} +EXPORT_SYMBOL_GPL(dpll_muxed_pin_register); + +int dpll_muxed_pin_deregister(struct dpll_pin *parent_pin, struct dpll_pin *pin) +{ + int ret = 0; + + if (xa_empty(&parent_pin->muxed_pins)) + return -ENOENT; + + mutex_lock(&parent_pin->lock); + ret = pin_deregister(&parent_pin->muxed_pins, pin); + mutex_unlock(&parent_pin->lock); + + if (!ret) { + mutex_lock(&pin->lock); + pin->ref_count--; + mutex_unlock(&pin->lock); + dpll_notify_muxed_pin_deregister(parent_pin, pin->id); + } + + return ret; +} +EXPORT_SYMBOL_GPL(dpll_muxed_pin_deregister); + +struct dpll_pin *dpll_pin_get_by_name(struct dpll_device *dpll, + const char *name) +{ + struct dpll_pin *pos, *pin = NULL; + unsigned long index; + + mutex_lock(&dpll->lock); + xa_for_each(&dpll->pins, index, pos) { + if (!strncmp(pos->name, name, PIN_NAME_LENGTH)) { + pin = pos; + break; + } + } + mutex_unlock(&dpll->lock); + + return pin; +} +EXPORT_SYMBOL_GPL(dpll_pin_get_by_name); + +struct dpll_pin *dpll_pin_get_by_id(struct dpll_device *dpll, int id) +{ + struct dpll_pin *pos, *pin = NULL; + unsigned long index; + + xa_for_each(&dpll->pins, index, pos) { + if (pos->id == id) { + pin = pos; + break; + } + } + + return pin; +} +EXPORT_SYMBOL_GPL(dpll_pin_get_by_id); + static int __init dpll_init(void) { int ret; diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 4b6fc9eb228fb..7ff64df735d93 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -10,15 +10,50 @@ #include "dpll_netlink.h" +#define PIN_SOURCE XA_MARK_1 +#define PIN_OUTPUT XA_MARK_2 +#define PIN_TYPE_TO_MARK(t) (t == DPLL_PIN_TYPE_SOURCE ? PIN_SOURCE : PIN_OUTPUT) +#define __FOR_EACH_PIN(dpll, index, count, pin, type) \ + count = type == DPLL_PIN_TYPE_SOURCE ? \ + dpll->sources_count : dpll->outputs_count; \ + xa_for_each_marked(&dpll->pins, index, pin, PIN_TYPE_TO_MARK(type)) +#define FOR_EACH_SOURCE(dpll, index, count, pin) \ + __FOR_EACH_PIN(dpll, index, count, pin, DPLL_PIN_TYPE_SOURCE) +#define FOR_EACH_OUTPUT(dpll, index, count, pin) \ + __FOR_EACH_PIN(dpll, index, count, pin, DPLL_PIN_TYPE_OUTPUT) + +/** + * struct dpll_pin - structure for a dpll pin + * @id: unique id number for each pin + * @type: type of the pin + * @ref_count: count number of dpll's that registered this pin + * @ops: operations this &dpll_pin supports + * @lock: mutex to serialize operations + * @priv: pointer to private information of owner + * @muxed_pins: array of muxed pins + * @name: name to distinguish the pin + */ +struct dpll_pin { + int id; + enum dpll_pin_type type; + int ref_count; + struct dpll_pin_ops *ops; + struct mutex lock; + void *priv; + struct xarray muxed_pins; + char name[PIN_NAME_LENGTH]; +}; + /** * struct dpll_device - structure for a DPLL device * @id: unique id number for each edvice * @dev: &struct device for this dpll device - * @sources_count: amount of input sources this dpll_device supports - * @outputs_count: amount of outputs this dpll_device supports + * @sources_count: amount of input sources this dpll_device supports + * @outputs_count: amount of outputs this dpll_device supports * @ops: operations this &dpll_device supports * @lock: mutex to serialize operations * @priv: pointer to private information of owner + * @pins: list of pointers to pins registered with this dpll */ struct dpll_device { int id; @@ -28,6 +63,7 @@ struct dpll_device { struct dpll_device_ops *ops; struct mutex lock; void *priv; + struct xarray pins; }; #define to_dpll_device(_dev) \ @@ -38,4 +74,7 @@ int for_each_dpll_device(int id, int (*cb)(struct dpll_device *, void *), struct dpll_device *dpll_device_get_by_id(int id); struct dpll_device *dpll_device_get_by_name(const char *name); void dpll_device_unregister(struct dpll_device *dpll); +void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, + struct dpll_pin_ops *ops, void *priv, + const char *name, int id); #endif diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index e3604c10b59e2..5c43da0b4d7dd 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -23,24 +23,24 @@ static const struct nla_policy dpll_genl_get_policy[] = { [DPLLA_DEVICE_ID] = { .type = NLA_U32 }, [DPLLA_DEVICE_NAME] = { .type = NLA_STRING, .len = DPLL_NAME_LENGTH }, - [DPLLA_DEVICE_SRC_SELECT_MODE] = { .type = NLA_U32 }, [DPLLA_FLAGS] = { .type = NLA_U32 }, }; +static const struct nla_policy dpll_genl_set_pin_type_policy[] = { + [DPLLA_DEVICE_ID] = { .type = NLA_U32 }, + [DPLLA_PIN_ID] = { .type = NLA_U32 }, + [DPLLA_PIN_TYPE] = { .type = NLA_U32 }, +}; + static const struct nla_policy dpll_genl_set_source_policy[] = { [DPLLA_DEVICE_ID] = { .type = NLA_U32 }, - [DPLLA_SOURCE_ID] = { .type = NLA_U32 }, - [DPLLA_SOURCE_TYPE] = { .type = NLA_U32 }, - [DPLLA_SOURCE_NAME] = { .type = NLA_STRING, - .len = DPLL_NAME_LENGTH }, + [DPLLA_PIN_ID] = { .type = NLA_U32 }, }; -static const struct nla_policy dpll_genl_set_output_policy[] = { +static const struct nla_policy dpll_genl_set_pin_flags_policy[] = { [DPLLA_DEVICE_ID] = { .type = NLA_U32 }, - [DPLLA_OUTPUT_ID] = { .type = NLA_U32 }, - [DPLLA_OUTPUT_TYPE] = { .type = NLA_U32 }, - [DPLLA_OUTPUT_NAME] = { .type = NLA_STRING, - .len = DPLL_NAME_LENGTH }, + [DPLLA_PIN_ID] = { .type = NLA_U32 }, + [DPLLA_PIN_FLAGS] = { .type = NLA_U32 }, }; static const struct nla_policy dpll_genl_set_src_select_mode_policy[] = { @@ -50,23 +50,26 @@ static const struct nla_policy dpll_genl_set_src_select_mode_policy[] = { static const struct nla_policy dpll_genl_set_source_prio_policy[] = { [DPLLA_DEVICE_ID] = { .type = NLA_U32 }, - [DPLLA_SOURCE_ID] = { .type = NLA_U32 }, - [DPLLA_SOURCE_PRIO] = { .type = NLA_U32 }, + [DPLLA_PIN_ID] = { .type = NLA_U32 }, + [DPLLA_SOURCE_PIN_PRIO] = { .type = NLA_U32 }, }; struct param { struct netlink_callback *cb; struct dpll_device *dpll; + struct dpll_pin *dpll_shared_pin; struct sk_buff *msg; int dpll_id; int dpll_src_select_mode; - int dpll_source_id; - int dpll_source_type; + int dpll_pin_type; int dpll_source_prio; - int dpll_output_id; - int dpll_output_type; + int dpll_pin_id; + int dpll_pin_flags; + int dpll_parent_pin_id; int dpll_status; int dpll_event_group; + int dpll_event; + const char *dpll_name; }; @@ -102,45 +105,44 @@ static int __dpll_cmd_device_dump_one(struct dpll_device *dpll, static int __dpll_cmd_dump_sources(struct dpll_device *dpll, struct sk_buff *msg) { - int i, ret = 0, type, prio; + int ret = 0, type, prio, count; struct nlattr *src_attr; - const char *name; + struct dpll_pin *pin; + unsigned long i; - for (i = 0; i < dpll->sources_count; i++) { + FOR_EACH_SOURCE(dpll, i, count, pin) { src_attr = nla_nest_start(msg, DPLLA_SOURCE); if (!src_attr) { ret = -EMSGSIZE; break; } - type = dpll->ops->get_source_type(dpll, i); - if (nla_put_u32(msg, DPLLA_SOURCE_ID, i) || - nla_put_u32(msg, DPLLA_SOURCE_TYPE, type)) { + type = pin->ops->get_type(pin); + if (nla_put_u32(msg, DPLLA_PIN_ID, pin->id) || + nla_put_u32(msg, DPLLA_PIN_TYPE, type)) { nla_nest_cancel(msg, src_attr); ret = -EMSGSIZE; break; } - if (dpll->ops->get_source_supported) { + if (pin->ops->is_type_supported) { for (type = 0; type <= DPLL_TYPE_MAX; type++) { - ret = dpll->ops->get_source_supported(dpll, i, type); - if (ret && nla_put_u32(msg, DPLLA_SOURCE_SUPPORTED, type)) { + ret = pin->ops->is_type_supported(pin, type); + if (ret && nla_put_u32(msg, DPLLA_PIN_TYPE_SUPPORTED, type)) { ret = -EMSGSIZE; break; } } ret = 0; } - if (dpll->ops->get_source_prio) { - prio = dpll->ops->get_source_prio(dpll, i); - if (nla_put_u32(msg, DPLLA_SOURCE_PRIO, prio)) { + if (pin->ops->get_prio) { + prio = pin->ops->get_prio(pin, dpll); + if (nla_put_u32(msg, DPLLA_SOURCE_PIN_PRIO, prio)) { nla_nest_cancel(msg, src_attr); ret = -EMSGSIZE; break; } } - if (dpll->ops->get_source_name) { - name = dpll->ops->get_source_name(dpll, i); - if (name && nla_put_string(msg, DPLLA_SOURCE_NAME, - name)) { + if (pin->name) { + if (nla_put_string(msg, DPLLA_PIN_NAME, pin->name)) { nla_nest_cancel(msg, src_attr); ret = -EMSGSIZE; break; @@ -155,42 +157,40 @@ static int __dpll_cmd_dump_sources(struct dpll_device *dpll, static int __dpll_cmd_dump_outputs(struct dpll_device *dpll, struct sk_buff *msg) { + int ret = 0, type, count; struct nlattr *out_attr; - int i, ret = 0, type; - const char *name; + struct dpll_pin *pin; + unsigned long i; - for (i = 0; i < dpll->outputs_count; i++) { + FOR_EACH_OUTPUT(dpll, i, count, pin) { out_attr = nla_nest_start(msg, DPLLA_OUTPUT); if (!out_attr) { ret = -EMSGSIZE; break; } - type = dpll->ops->get_output_type(dpll, i); - if (nla_put_u32(msg, DPLLA_OUTPUT_ID, i) || - nla_put_u32(msg, DPLLA_OUTPUT_TYPE, type)) { + type = pin->ops->get_type(pin); + if (nla_put_u32(msg, DPLLA_PIN_ID, pin->id) || + nla_put_u32(msg, DPLLA_PIN_TYPE, type)) { nla_nest_cancel(msg, out_attr); ret = -EMSGSIZE; break; } - if (dpll->ops->get_output_supported) { + if (pin->ops->is_type_supported) { for (type = 0; type <= DPLL_TYPE_MAX; type++) { - ret = dpll->ops->get_output_supported(dpll, i, type); - if (ret && nla_put_u32(msg, DPLLA_OUTPUT_SUPPORTED, type)) { + ret = pin->ops->is_type_supported(pin, type); + if (ret && nla_put_u32(msg, DPLLA_PIN_TYPE_SUPPORTED, type)) { ret = -EMSGSIZE; break; } } ret = 0; } - if (dpll->ops->get_output_name) { - name = dpll->ops->get_output_name(dpll, i); - if (name && nla_put_string(msg, DPLLA_OUTPUT_NAME, - name)) { - nla_nest_cancel(msg, out_attr); - ret = -EMSGSIZE; - break; - } + if (nla_put_string(msg, DPLLA_PIN_NAME, pin->name)) { + nla_nest_cancel(msg, out_attr); + ret = -EMSGSIZE; + break; } + nla_nest_end(msg, out_attr); } @@ -265,13 +265,13 @@ dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg, if (ret) goto out_unlock; - if (flags & DPLL_FLAG_SOURCES && dpll->ops->get_source_type) { + if (flags & DPLL_FLAG_SOURCES) { ret = __dpll_cmd_dump_sources(dpll, msg); if (ret) goto out_unlock; } - if (flags & DPLL_FLAG_OUTPUTS && dpll->ops->get_output_type) { + if (flags & DPLL_FLAG_OUTPUTS) { ret = __dpll_cmd_dump_outputs(dpll, msg); if (ret) goto out_unlock; @@ -294,54 +294,85 @@ dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg, return ret; } -static int dpll_genl_cmd_set_source(struct sk_buff *skb, struct genl_info *info) +static int dpll_genl_cmd_set_pin_type(struct sk_buff *skb, + struct genl_info *info) { struct dpll_device *dpll = info->user_ptr[0]; struct nlattr **attrs = info->attrs; - int ret = 0, src_id, type; + int ret = 0, pin_id, type; + struct dpll_pin *pin; - if (!attrs[DPLLA_SOURCE_ID] || - !attrs[DPLLA_SOURCE_TYPE]) + if (!attrs[DPLLA_PIN_ID] || + !attrs[DPLLA_PIN_TYPE]) return -EINVAL; - if (!dpll->ops->set_source_type) - return -EOPNOTSUPP; - - src_id = nla_get_u32(attrs[DPLLA_SOURCE_ID]); - type = nla_get_u32(attrs[DPLLA_SOURCE_TYPE]); + pin_id = nla_get_u32(attrs[DPLLA_PIN_ID]); + type = nla_get_u32(attrs[DPLLA_PIN_TYPE]); mutex_lock(&dpll->lock); - ret = dpll->ops->set_source_type(dpll, src_id, type); + pin = dpll_pin_get_by_id(dpll, pin_id); + if (!pin || !pin->ops || !pin->ops->set_type) + ret = -EOPNOTSUPP; + else + ret = pin->ops->set_type(pin, type); mutex_unlock(&dpll->lock); if (!ret) - dpll_notify_source_change(dpll->id, src_id, type); + dpll_notify_pin_type_change(dpll->id, pin_id, type); return ret; } -static int dpll_genl_cmd_set_output(struct sk_buff *skb, struct genl_info *info) +static int dpll_genl_cmd_set_source(struct sk_buff *skb, struct genl_info *info) { struct dpll_device *dpll = info->user_ptr[0]; struct nlattr **attrs = info->attrs; - int ret = 0, out_id, type; + int ret = 0, src_id; + struct dpll_pin *pin; - if (!attrs[DPLLA_OUTPUT_ID] || - !attrs[DPLLA_OUTPUT_TYPE]) + if (!attrs[DPLLA_PIN_ID]) return -EINVAL; - if (!dpll->ops->set_output_type) - return -EOPNOTSUPP; + src_id = nla_get_u32(attrs[DPLLA_PIN_ID]); + + mutex_lock(&dpll->lock); + pin = dpll_pin_get_by_id(dpll, src_id); + if (!pin || !pin->ops || !pin->ops->set_source) + ret = -EOPNOTSUPP; + else + ret = pin->ops->set_source(pin, dpll, src_id); + mutex_unlock(&dpll->lock); + + if (!ret) + dpll_notify_source_change(dpll->id, src_id); + + return ret; +} + +static int dpll_genl_cmd_set_pin_flags(struct sk_buff *skb, struct genl_info *info) +{ + struct dpll_device *dpll = info->user_ptr[0]; + struct nlattr **attrs = info->attrs; + int ret = 0, pin_id, flags; + struct dpll_pin *pin; - out_id = nla_get_u32(attrs[DPLLA_OUTPUT_ID]); - type = nla_get_u32(attrs[DPLLA_OUTPUT_TYPE]); + if (!attrs[DPLLA_PIN_ID] || + !attrs[DPLLA_PIN_FLAGS]) + return -EINVAL; + + pin_id = nla_get_u32(attrs[DPLLA_PIN_ID]); + flags = nla_get_u32(attrs[DPLLA_PIN_FLAGS]); mutex_lock(&dpll->lock); - ret = dpll->ops->set_output_type(dpll, out_id, type); + pin = dpll_pin_get_by_id(dpll, pin_id); + if (!pin || !pin->ops || !pin->ops->set_flags) + ret = -EOPNOTSUPP; + else + ret = pin->ops->set_flags(pin, dpll, flags); mutex_unlock(&dpll->lock); if (!ret) - dpll_notify_output_change(dpll->id, out_id, type); + dpll_notify_pin_flags_change(dpll->id, pin_id, flags); return ret; } @@ -351,19 +382,21 @@ static int dpll_genl_cmd_set_source_prio(struct sk_buff *skb, struct genl_info * struct dpll_device *dpll = info->user_ptr[0]; struct nlattr **attrs = info->attrs; int ret = 0, src_id, prio; + struct dpll_pin *pin; - if (!attrs[DPLLA_SOURCE_ID] || - !attrs[DPLLA_SOURCE_PRIO]) + if (!attrs[DPLLA_PIN_ID] || + !attrs[DPLLA_SOURCE_PIN_PRIO]) return -EINVAL; - if (!dpll->ops->set_source_prio) - return -EOPNOTSUPP; - - src_id = nla_get_u32(attrs[DPLLA_SOURCE_ID]); - prio = nla_get_u32(attrs[DPLLA_SOURCE_PRIO]); + src_id = nla_get_u32(attrs[DPLLA_PIN_ID]); + prio = nla_get_u32(attrs[DPLLA_SOURCE_PIN_PRIO]); mutex_lock(&dpll->lock); - ret = dpll->ops->set_source_prio(dpll, src_id, prio); + pin = dpll_pin_get_by_id(dpll, src_id); + if (!pin || !pin->ops || !pin->ops->set_prio) + ret = -EOPNOTSUPP; + else + ret = pin->ops->set_prio(pin, dpll, prio); mutex_unlock(&dpll->lock); if (!ret) @@ -505,18 +538,25 @@ static const struct genl_ops dpll_genl_ops[] = { .maxattr = ARRAY_SIZE(dpll_genl_get_policy) - 1, }, { - .cmd = DPLL_CMD_SET_SOURCE_TYPE, + .cmd = DPLL_CMD_SET_SOURCE, .flags = GENL_UNS_ADMIN_PERM, .doit = dpll_genl_cmd_set_source, .policy = dpll_genl_set_source_policy, .maxattr = ARRAY_SIZE(dpll_genl_set_source_policy) - 1, }, { - .cmd = DPLL_CMD_SET_OUTPUT_TYPE, + .cmd = DPLL_CMD_SET_PIN_TYPE, .flags = GENL_UNS_ADMIN_PERM, - .doit = dpll_genl_cmd_set_output, - .policy = dpll_genl_set_output_policy, - .maxattr = ARRAY_SIZE(dpll_genl_set_output_policy) - 1, + .doit = dpll_genl_cmd_set_pin_type, + .policy = dpll_genl_set_pin_type_policy, + .maxattr = ARRAY_SIZE(dpll_genl_set_pin_type_policy) - 1, + }, + { + .cmd = DPLL_CMD_SET_PIN_FLAGS, + .flags = GENL_UNS_ADMIN_PERM, + .doit = dpll_genl_cmd_set_pin_flags, + .policy = dpll_genl_set_pin_flags_policy, + .maxattr = ARRAY_SIZE(dpll_genl_set_pin_flags_policy) - 1, }, { .cmd = DPLL_CMD_SET_SRC_SELECT_MODE, @@ -565,7 +605,7 @@ static int dpll_event_device_delete(struct param *p) static int dpll_event_status(struct param *p) { if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || - nla_put_u32(p->msg, DPLLA_LOCK_STATUS, p->dpll_status)) + nla_put_u32(p->msg, DPLLA_LOCK_STATUS, p->dpll_status)) return -EMSGSIZE; return 0; @@ -574,18 +614,17 @@ static int dpll_event_status(struct param *p) static int dpll_event_source_change(struct param *p) { if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || - nla_put_u32(p->msg, DPLLA_SOURCE_ID, p->dpll_source_id) || - nla_put_u32(p->msg, DPLLA_SOURCE_TYPE, p->dpll_source_type)) + nla_put_u32(p->msg, DPLLA_PIN_ID, p->dpll_pin_id)) return -EMSGSIZE; return 0; } -static int dpll_event_output_change(struct param *p) +static int dpll_event_pin_flags_change(struct param *p) { if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || - nla_put_u32(p->msg, DPLLA_OUTPUT_ID, p->dpll_output_id) || - nla_put_u32(p->msg, DPLLA_OUTPUT_TYPE, p->dpll_output_type)) + nla_put_u32(p->msg, DPLLA_PIN_ID, p->dpll_pin_id) || + nla_put_u32(p->msg, DPLLA_PIN_FLAGS, p->dpll_pin_flags)) return -EMSGSIZE; return 0; @@ -594,8 +633,8 @@ static int dpll_event_output_change(struct param *p) static int dpll_event_source_prio(struct param *p) { if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || - nla_put_u32(p->msg, DPLLA_SOURCE_ID, p->dpll_source_id) || - nla_put_u32(p->msg, DPLLA_SOURCE_PRIO, p->dpll_source_prio)) + nla_put_u32(p->msg, DPLLA_PIN_ID, p->dpll_pin_id) || + nla_put_u32(p->msg, DPLLA_SOURCE_PIN_PRIO, p->dpll_source_prio)) return -EMSGSIZE; return 0; @@ -611,22 +650,46 @@ static int dpll_event_select_mode(struct param *p) return 0; } +static int dpll_event_pin_register(struct param *p) +{ + if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || + nla_put_u32(p->msg, DPLLA_PIN_ID, p->dpll_pin_id)) + return -EMSGSIZE; + + return 0; +} + +static int dpll_event_muxed_pin_register(struct param *p) +{ + if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) || + nla_put_u32(p->msg, DPLLA_PIN_ID, p->dpll_pin_id) || + nla_put_u32(p->msg, DPLLA_PARENT_PIN_ID, p->dpll_parent_pin_id)) + return -EMSGSIZE; + + return 0; +} + static const cb_t event_cb[] = { [DPLL_EVENT_DEVICE_CREATE] = dpll_event_device_create, [DPLL_EVENT_DEVICE_DELETE] = dpll_event_device_delete, [DPLL_EVENT_STATUS_LOCKED] = dpll_event_status, [DPLL_EVENT_STATUS_UNLOCKED] = dpll_event_status, [DPLL_EVENT_SOURCE_CHANGE] = dpll_event_source_change, - [DPLL_EVENT_OUTPUT_CHANGE] = dpll_event_output_change, - [DPLL_EVENT_SOURCE_PRIO] = dpll_event_source_prio, - [DPLL_EVENT_SELECT_MODE] = dpll_event_select_mode, + [DPLL_EVENT_PIN_TYPE_CHANGE] = dpll_event_pin_flags_change, + [DPLL_EVENT_PIN_FLAGS_CHANGE] = dpll_event_pin_flags_change, + [DPLL_EVENT_SOURCE_PRIO_CHANGE] = dpll_event_source_prio, + [DPLL_EVENT_SELECT_MODE_CHANGE] = dpll_event_select_mode, + [DPLL_EVENT_PIN_REGISTER] = dpll_event_pin_register, + [DPLL_EVENT_PIN_DEREGISTER] = dpll_event_pin_register, + [DPLL_EVENT_MUXED_PIN_REGISTER] = dpll_event_muxed_pin_register, + [DPLL_EVENT_MUXED_PIN_DEREGISTER] = dpll_event_muxed_pin_register, }; /* * Generic netlink DPLL event encoding */ static int dpll_send_event(enum dpll_genl_event event, - struct param *p) + struct param *p) { struct sk_buff *msg; int ret = -EMSGSIZE; @@ -692,23 +755,33 @@ int dpll_notify_status_unlocked(int dpll_id) } EXPORT_SYMBOL_GPL(dpll_notify_status_unlocked); -int dpll_notify_source_change(int dpll_id, int source_id, int source_type) +int dpll_notify_source_change(int dpll_id, int source_id) { - struct param p = { .dpll_id = dpll_id, .dpll_source_id = source_id, - .dpll_source_type = source_type, .dpll_event_group = 1 }; + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = source_id, + .dpll_event_group = 1 }; return dpll_send_event(DPLL_EVENT_SOURCE_CHANGE, &p); } EXPORT_SYMBOL_GPL(dpll_notify_source_change); -int dpll_notify_output_change(int dpll_id, int output_id, int output_type) + +int dpll_notify_pin_type_change(int dpll_id, int pin_id, int output_type) { - struct param p = { .dpll_id = dpll_id, .dpll_output_id = output_id, - .dpll_output_type = output_type, .dpll_event_group = 2 }; + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = pin_id, + .dpll_pin_type = output_type, .dpll_event_group = 2 }; - return dpll_send_event(DPLL_EVENT_OUTPUT_CHANGE, &p); + return dpll_send_event(DPLL_EVENT_PIN_TYPE_CHANGE, &p); } -EXPORT_SYMBOL_GPL(dpll_notify_output_change); +EXPORT_SYMBOL_GPL(dpll_notify_pin_type_change); + +int dpll_notify_pin_flags_change(int dpll_id, int pin_id, int flags) +{ + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = pin_id, + .dpll_pin_flags = flags, .dpll_event_group = 2 }; + + return dpll_send_event(DPLL_EVENT_PIN_FLAGS_CHANGE, &p); +} +EXPORT_SYMBOL_GPL(dpll_notify_pin_flags_change); int dpll_notify_source_select_mode_change(int dpll_id, int new_mode) { @@ -716,20 +789,77 @@ int dpll_notify_source_select_mode_change(int dpll_id, int new_mode) .dpll_src_select_mode = new_mode, .dpll_event_group = 0 }; - return dpll_send_event(DPLL_EVENT_SELECT_MODE, &p); + return dpll_send_event(DPLL_EVENT_SELECT_MODE_CHANGE, &p); } EXPORT_SYMBOL_GPL(dpll_notify_source_select_mode_change); int dpll_notify_source_prio_change(int dpll_id, int source_id, int prio) { - struct param p = { .dpll_id = dpll_id, .dpll_source_id = source_id, + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = source_id, .dpll_source_prio = prio, .dpll_event_group = 1 }; - return dpll_send_event(DPLL_EVENT_SOURCE_PRIO, &p); + return dpll_send_event(DPLL_EVENT_SOURCE_PRIO_CHANGE, &p); } EXPORT_SYMBOL_GPL(dpll_notify_source_prio_change); +int dpll_notify_pin_register(int dpll_id, int pin_id) +{ + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = pin_id, + .dpll_event_group = 1 }; + + return dpll_send_event(DPLL_EVENT_PIN_REGISTER, &p); +} +EXPORT_SYMBOL_GPL(dpll_notify_pin_register); + +int dpll_notify_pin_deregister(int dpll_id, int pin_id) +{ + struct param p = { .dpll_id = dpll_id, .dpll_pin_id = pin_id, + .dpll_event_group = 1 }; + + return dpll_send_event(DPLL_EVENT_PIN_DEREGISTER, &p); +} +EXPORT_SYMBOL_GPL(dpll_notify_pin_deregister); + +static int dpll_shared_pins_notify_cb(struct dpll_device *dpll, void *data) +{ + struct param *p = data; + struct dpll_pin *pin; + unsigned long i; + + FOR_EACH_SOURCE(dpll, i, dpll->sources_count, pin) + if (pin == p->dpll_shared_pin) { + p->dpll_id = dpll->id; + return dpll_send_event(p->dpll_event, p); + } + + return 0; +} + +int dpll_notify_muxed_pin_register(struct dpll_pin *parent_pin, int pin_id) +{ + struct param p = { .dpll_pin_id = pin_id, + .dpll_shared_pin = parent_pin, + .dpll_parent_pin_id = parent_pin->id, + .dpll_event_group = 1, + .dpll_event = DPLL_EVENT_MUXED_PIN_REGISTER }; + + return for_each_dpll_device(0, dpll_shared_pins_notify_cb, &p); +} +EXPORT_SYMBOL_GPL(dpll_notify_muxed_pin_register); + +int dpll_notify_muxed_pin_deregister(struct dpll_pin *parent_pin, int pin_id) +{ + struct param p = { .dpll_pin_id = pin_id, + .dpll_shared_pin = parent_pin, + .dpll_parent_pin_id = parent_pin->id, + .dpll_event_group = 1, + .dpll_event = DPLL_EVENT_MUXED_PIN_DEREGISTER}; + + return for_each_dpll_device(0, dpll_shared_pins_notify_cb, &p); +} +EXPORT_SYMBOL_GPL(dpll_notify_muxed_pin_deregister); + int __init dpll_netlink_init(void) { return genl_register_family(&dpll_gnl_family); diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index 4befbefe95325..39c1a1be222d7 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -659,7 +659,7 @@ static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, { struct dpll_pin_ops *ops; enum dpll_pin_type type; - int ret, i, alloc_size; + int ret, i; if (inputs) { type = DPLL_PIN_TYPE_SOURCE; @@ -670,23 +670,11 @@ static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, ops = &ice_synce_output_ops; } - alloc_size = sizeof(dpll_pin) * count; - pins->pin = kmalloc(alloc_size, GFP_KERNEL); - for (i = 0; i < count; i++) { - pins[i].pin.ops = ops; - pins[i].pin.id = id; - pins[i].pin.type = type; - pins[i].pin.priv = priv; - if (inputs) - pins[i].pin.name = "source"; - else - pins[i].pin.name = "output"; - - ret = dpll_pin_register(dpll, pins[i].pin); + dpll_init_pin(&pins->pin, type, ops, pf, NULL, i); + dpll_pin_register(dpll, pins->pin); if (ret) { ice_synce_release_pins(dpll, pins, i + 1); - return ret; } } @@ -795,6 +783,7 @@ static int ice_synce_update_dpll_state(struct ice_pf *pf, &se->current_source, &se->ref_state, &se->eec_mode, &se->phase_offset, &se->dpll_state); + if (ret) dev_err(ice_pf_to_dev(pf), "update dpll state failed, ret=%d %s\n", ret, ice_aq_str(pf->hw.adminq.sq_last_status)); @@ -945,4 +934,3 @@ void ice_synce_release(struct ice_pf *pf) mutex_unlock(&se->lock); mutex_destroy(&se->lock); } - diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index a01c0c7218025..8b4bb8e697eeb 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -3851,7 +3851,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) ptp_ocp_info(bp); devlink_register(devlink); - bp->dpll = dpll_device_alloc(&dpll_ops, "ocp", ARRAY_SIZE(bp->sma), ARRAY_SIZE(bp->sma), bp); + bp->dpll = dpll_device_alloc(&dpll_ops, "ocp", bp); if (!bp->dpll) { dev_err(&pdev->dev, "dpll_device_alloc failed\n"); return 0; diff --git a/include/linux/dpll.h b/include/linux/dpll.h index 2f4964dc28f05..133999be7f066 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -6,7 +6,10 @@ #ifndef __DPLL_H__ #define __DPLL_H__ +#define PIN_NAME_LENGTH 20 + struct dpll_device; +struct dpll_pin; struct dpll_device_ops { int (*get_status)(struct dpll_device *dpll); @@ -14,30 +17,197 @@ struct dpll_device_ops { int (*get_lock_status)(struct dpll_device *dpll); int (*get_source_select_mode)(struct dpll_device *dpll); int (*get_source_select_mode_supported)(struct dpll_device *dpll, int type); - int (*get_source_type)(struct dpll_device *dpll, int id); - int (*get_source_supported)(struct dpll_device *dpll, int id, int type); - int (*get_source_prio)(struct dpll_device *dpll, int id); - int (*get_output_type)(struct dpll_device *dpll, int id); - int (*get_output_supported)(struct dpll_device *dpll, int id, int type); - int (*set_source_type)(struct dpll_device *dpll, int id, int val); - int (*set_output_type)(struct dpll_device *dpll, int id, int val); int (*set_source_select_mode)(struct dpll_device *dpll, int mode); - int (*set_source_prio)(struct dpll_device *dpll, int id, int prio); - const char *(*get_source_name)(struct dpll_device *dpll, int id); - const char *(*get_output_name)(struct dpll_device *dpll, int id); + int (*get_source_type)(struct dpll_device *dpll, int sma); + int (*get_source_supported)(struct dpll_device *dpll, int sma, int type); + int (*get_output_type)(struct dpll_device *dpll, int sma); + int (*get_output_supported)(struct dpll_device, int sma, int type); +}; + +struct dpll_pin_ops { + int (*get_type)(struct dpll_pin *pin); + int (*is_type_supported)(struct dpll_pin *pin, int type); + int (*set_type)(struct dpll_pin *pin, int type); + int (*set_source)(struct dpll_pin *pin, struct dpll_device *dpll, int id); + int (*set_flags)(struct dpll_pin *pin, struct dpll_device *dpll, int flags); + int (*get_prio)(struct dpll_pin *pin, struct dpll_device *dpll); + int (*set_prio)(struct dpll_pin *pin, struct dpll_device *dpll, int prio); }; -struct dpll_device *dpll_device_alloc(struct dpll_device_ops *ops, const char *name, - int sources_count, int outputs_count, void *priv); +enum dpll_pin_type { + DPLL_PIN_TYPE_INVALID, + DPLL_PIN_TYPE_SOURCE, + DPLL_PIN_TYPE_OUTPUT, + DPLL_PIN_TYPE_MUX, + __DPLL_PIN_TYPE_MAX, +}; +#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1) + +struct dpll_device *dpll_device_alloc(struct dpll_device_ops *ops, + const char *name, void *priv); void dpll_device_register(struct dpll_device *dpll); void dpll_device_unregister(struct dpll_device *dpll); void dpll_device_free(struct dpll_device *dpll); void *dpll_priv(struct dpll_device *dpll); +void *pin_priv(struct dpll_pin *pin); +int pin_id(struct dpll_pin *pin); + +/* + * dpll_pin_alloc - allocate memory for a new dpll_pin object + * @ops: pointer to pin operations structure + * @id: id given by the owner, used to find pin by id + * @dpll_pin_type: type of a pin being allocated + * @name: human readable pin name of allocated pin + * @priv: private data of a registerer + * + * Allocate memory for a new pin and initialize it with its type, name, + * callbacks and private data pointer. + * + * Returns: + * * pointer to initialized pin - success, + * * ERR_PTR(-ENOMEM) - memory failure, + * * ERR_PTR(-EINVAL) - given pin type is invalid. + **/ +struct dpll_pin *dpll_pin_alloc(struct dpll_pin_ops *ops, int id, + enum dpll_pin_type type, + const char *name, void *priv); + +/* + * dpll_pin_register - register pin with a dpll device + * @dpll: pointer to dpll object to register pin with + * @pin: pointer to allocated pin object being registered with dpll + * + * Register previously allocated pin object with a dpll device. + * + * Return: + * * 0 - if pin was registered with a parent pin, + * * -ENOMEM - failed to allocate memory, + * * -EEXIST - pin already registered with this dpll, + * * -EBUSY - couldn't allocate id for a pin. + **/ +int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin); + +/* + * dpll_pin_deregister - deregister pin from a dpll device + * @dpll: pointer to dpll object to deregister pin from + * @pin: pointer to allocated pin object being deregistered from dpll + * + * Deregister previously registered pin object from a dpll device. + * + * Return: + * * 0 - pin was successfully deregistered from this dpll device, + * * -ENOENT - given pin was not registered with this dpll device, + * * -EINVAL - pin pointer is not valid. + **/ +int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin); + +/* + * dpll_pin_free - free memory allocated for a pin + * @dpll: pointer to dpll object that owns the pin + * @pin: pointer to allocated pin object being freed + * + * Shared pins must be deregistered from all dpll devices before freeing them, + * otherwise the memory won't be freed. + **/ +void dpll_pin_free(struct dpll_device *dpll, struct dpll_pin *pin); + +/* + * dpll_muxed_pin_free - free memory allocated for a pin + * @parent_pin: pointer to object that owns the pin + * @pin: pointer to allocated pin object being freed + * + * Free memory of deregistered pin object. + * Shared pins must be deregistered from all dpll devices before freeing them, + * otherwise memory won't be freed. + */ +void dpll_muxed_pin_free(struct dpll_pin *parent_pin, struct dpll_pin *pin); + +/* + * dpll_muxed_pin_register - register a pin to a muxed-type pin + * @parent_pin: pointer to object to deregister pin from + * @pin: pointer to allocated pin object being deregistered from dpll + * + * In case of multiplexed pins, register them under a single parent pin. + * + * Return: + * * 0 - if pin was registered with a parent pin, + * * -ENOMEM - failed to allocate memory, + * * -EEXIST - pin already registered with this parent pin, + * * -EBUSY - couldn't assign id for a pin. + **/ +int dpll_muxed_pin_register(struct dpll_pin *parent_pin, struct dpll_pin *pin); + +/* + * dpll_muxed_pin_deregister - deregister a pin from a muxed-type pin + * @parent_pin: pointer to object to deregister pin from + * @pin: pointer to allocated pin object being deregistered from dpll + * + * In case of multiplexed pins, deregister a pin from a parent pin. + * + * Return: + * * 0 - pin was successfully deregistered from a parent pin, + * * -ENOENT - given pin was not registered with this parent pin, + * * -EINVAL - pin pointer is not valid. + **/ +int dpll_muxed_pin_deregister(struct dpll_pin *parent_pin, struct dpll_pin *pin); + +/* + * dpll_device_get_by_name - find a dpll by its name + * @name: name of device + * + * Allows multiple driver instances using one physical DPLL to find + * and share already registered DPLL device. + * + * Return: pointer if device was found, NULL otherwise. + **/ +struct dpll_device *dpll_device_get_by_name(const char *name); + +/* + * dpll_pin_get_by_name - find a pin belonging to dpll by its name + * @name: name of device + * + * Allows multiple driver instances using one physical DPLL to find + * and share pin already registered with existing dpll device. + * + * Return: + * * pointer to existing pin if pin was found, + * * NULL if pin was not found. + **/ +struct dpll_pin *dpll_pin_get_by_name(struct dpll_device *dpll, const char *name); + +/* + * dpll_pin_get_by_id - find a pin by its id + * @name: name of device + * + * Allows multiple driver instances using one physical DPLL to find + * and share pin already registered with existing dpll device. + * + * Return: pointer if pin was found, NULL otherwise. + **/ +struct dpll_pin *dpll_pin_get_by_id(struct dpll_device *dpll, int id); + +/* + * dpll_init_pins - initialize data of dpll pins + * @pin: pointer to pointer to dpll object to initialize + * @type: the type of dpll pin + * @ops: pointer to dpll ops + * @priv: private data of an dpll pin object + * @name: human readable pin name + * @id: dpll pin id + **/ +void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, + struct dpll_pin_ops *ops, void *priv, const char *name, + int id); int dpll_notify_status_locked(int dpll_id); int dpll_notify_status_unlocked(int dpll_id); -int dpll_notify_source_change(int dpll_id, int source_id, int source_type); -int dpll_notify_output_change(int dpll_id, int output_id, int output_type); +int dpll_notify_source_change(int dpll_id, int pin_id); +int dpll_notify_pin_type_change(int dpll_id, int pin_id, int type); +int dpll_notify_pin_flags_change(int dpll_id, int pin_id, int flags); int dpll_notify_source_select_mode_change(int dpll_id, int source_select_mode); -int dpll_notify_source_prio_change(int dpll_id, int source_id, int prio); +int dpll_notify_source_prio_change(int dpll_id, int src_id, int prio); +int dpll_notify_pin_register(int dpll_id, int pin_id); +int dpll_notify_pin_deregister(int dpll_id, int pin_id); +int dpll_notify_muxed_pin_register(struct dpll_pin *parent_pin, int pin_id); +int dpll_notify_muxed_pin_deregister(struct dpll_pin *parent_pin, int pin_id); #endif diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index 59fc6ef81b408..db998ba408729 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -16,6 +16,10 @@ #define DPLL_FLAG_OUTPUTS 2 #define DPLL_FLAG_STATUS 4 +#define DPLL_PIN_FLAG_SOURCE 1 +#define DPLL_PIN_FLAG_OUTPUT 2 +#define DPLL_PIN_FLAG_ENABLED 4 + /* Attributes of dpll_genl_family */ enum dpll_genl_attr { DPLLA_UNSPEC, @@ -24,16 +28,14 @@ enum dpll_genl_attr { DPLLA_DEVICE_SRC_SELECT_MODE, DPLLA_DEVICE_SRC_SELECT_MODE_SUPPORTED, DPLLA_SOURCE, - DPLLA_SOURCE_ID, - DPLLA_SOURCE_TYPE, - DPLLA_SOURCE_NAME, - DPLLA_SOURCE_SUPPORTED, - DPLLA_SOURCE_PRIO, DPLLA_OUTPUT, - DPLLA_OUTPUT_ID, - DPLLA_OUTPUT_TYPE, - DPLLA_OUTPUT_NAME, - DPLLA_OUTPUT_SUPPORTED, + DPLLA_PIN_ID, + DPLLA_PIN_NAME, + DPLLA_PIN_TYPE, + DPLLA_PIN_FLAGS, + DPLLA_PIN_TYPE_SUPPORTED, + DPLLA_PARENT_PIN_ID, + DPLLA_SOURCE_PIN_PRIO, DPLLA_STATUS, DPLLA_TEMP, DPLLA_LOCK_STATUS, @@ -48,6 +50,7 @@ enum dpll_genl_status { DPLL_STATUS_NONE, DPLL_STATUS_CALIBRATING, DPLL_STATUS_LOCKED, + DPLL_STATUS_HOLDOVER, __DPLL_STATUS_MAX, }; @@ -83,14 +86,19 @@ enum dpll_genl_lock_status { /* Events of dpll_genl_family */ enum dpll_genl_event { DPLL_EVENT_UNSPEC, - DPLL_EVENT_DEVICE_CREATE, /* DPLL device creation */ - DPLL_EVENT_DEVICE_DELETE, /* DPLL device deletion */ - DPLL_EVENT_STATUS_LOCKED, /* DPLL device locked to source */ - DPLL_EVENT_STATUS_UNLOCKED, /* DPLL device freerun */ - DPLL_EVENT_SOURCE_CHANGE, /* DPLL device source changed */ - DPLL_EVENT_OUTPUT_CHANGE, /* DPLL device output changed */ - DPLL_EVENT_SOURCE_PRIO, - DPLL_EVENT_SELECT_MODE, + DPLL_EVENT_DEVICE_CREATE, /* DPLL device creation */ + DPLL_EVENT_DEVICE_DELETE, /* DPLL device deletion */ + DPLL_EVENT_STATUS_LOCKED, /* DPLL device locked to source */ + DPLL_EVENT_STATUS_UNLOCKED, /* DPLL device freerun or holdover */ + DPLL_EVENT_SOURCE_CHANGE, /* DPLL device source changed */ + DPLL_EVENT_PIN_TYPE_CHANGE, /* DPLL device pin type changed */ + DPLL_EVENT_PIN_FLAGS_CHANGE, /* DPLL device output changed */ + DPLL_EVENT_SOURCE_PRIO_CHANGE, /* DPLL device source pin prio changed */ + DPLL_EVENT_SELECT_MODE_CHANGE, /* DPLL device select source mode changed */ + DPLL_EVENT_PIN_REGISTER, /* DPLL device registered new pin */ + DPLL_EVENT_PIN_DEREGISTER, /* DPLL device deregistered a pin */ + DPLL_EVENT_MUXED_PIN_REGISTER, /* DPLL device registered new pin within parent pin */ + DPLL_EVENT_MUXED_PIN_DEREGISTER,/* DPLL device deregistered a pin from a parent pin */ __DPLL_EVENT_MAX, }; @@ -100,9 +108,10 @@ enum dpll_genl_event { enum dpll_genl_cmd { DPLL_CMD_UNSPEC, DPLL_CMD_DEVICE_GET, /* List of DPLL devices id */ - DPLL_CMD_SET_SOURCE_TYPE, /* Set the DPLL device source type */ - DPLL_CMD_SET_OUTPUT_TYPE, /* Set the DPLL device output type */ - DPLL_CMD_SET_SRC_SELECT_MODE,/* Set mode for selection of a source */ + DPLL_CMD_SET_SOURCE, /* Set the DPLL device source */ + DPLL_CMD_SET_PIN_TYPE, /* Set the DPLL device source type */ + DPLL_CMD_SET_PIN_FLAGS, /* Set the DPLL device output */ + DPLL_CMD_SET_SRC_SELECT_MODE, /* Set mode for selection of a source */ DPLL_CMD_SET_SOURCE_PRIO, /* Set priority of a source */ __DPLL_CMD_MAX, From 5691c6580add0f53ecdd7bdd89f0e00cda302284 Mon Sep 17 00:00:00 2001 From: Milena Date: Wed, 19 Oct 2022 15:50:34 +0200 Subject: [PATCH 3/6] dpll: move set priority to pin instead of dpll (#2) Previous implementation created ops for dedicated to pin object that allows to set/get pin priority. However priority of the pin may differ between various dplls. That is why this feature has been moved to dpll ops. Co-developed-by: Arkadiusz Kubalewski Signed-off-by: Arkadiusz Kubalewski Signed-off-by: Milena Olech Signed-off-by: Arkadiusz Kubalewski Signed-off-by: Milena Olech --- drivers/dpll/dpll_core.c | 19 +++++++------------ drivers/dpll/dpll_core.h | 2 +- drivers/dpll/dpll_netlink.c | 8 ++++---- drivers/net/ethernet/intel/ice/ice_synce.c | 14 +++++++------- include/linux/dpll.h | 4 ++-- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 0b5f499cfc4d5..f56a283ea35de 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -190,6 +190,7 @@ void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, (*pin)->type = type; (*pin)->priv = priv; (*pin)->id = id; + refcount_set(&((*pin)->ref_count), 0); if (name) snprintf((*pin)->name, PIN_NAME_LENGTH, "%s", name); else @@ -241,7 +242,7 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin) mutex_lock(&dpll->lock); ret = pin_register(&dpll->pins, pin); if (!ret) { - pin->ref_count++; + refcount_inc(&pin->ref_count); change_pin_count(dpll, pin, true); xa_set_mark(&dpll->pins, dpll->id, DPLL_REGISTERED); } @@ -287,9 +288,7 @@ int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin) mutex_unlock(&dpll->lock); if (!ret) { - mutex_lock(&pin->lock); - pin->ref_count--; - mutex_unlock(&pin->lock); + refcount_dec(&pin->ref_count); dpll_notify_pin_deregister(dpll->id, pin->id); } @@ -311,7 +310,7 @@ void dpll_pin_free(struct dpll_device *dpll, struct dpll_pin *pin) if (!pin_found) return; - if (pin->ref_count) + if (refcount_read(&pin->ref_count) != 0) return; xa_destroy(&pin->muxed_pins); @@ -333,7 +332,7 @@ void dpll_muxed_pin_free(struct dpll_pin *parent_pin, struct dpll_pin *pin) if (!pin_found) return; - if (pin->ref_count) + if (refcount_read(&pin->ref_count) != 0) return; xa_destroy(&pin->muxed_pins); @@ -348,9 +347,7 @@ int dpll_muxed_pin_register(struct dpll_pin *parent_pin, struct dpll_pin *pin) ret = pin_register(&parent_pin->muxed_pins, pin); if (!ret) { - mutex_lock(&pin->lock); - pin->ref_count++; - mutex_unlock(&pin->lock); + refcount_inc(&pin->ref_count); dpll_notify_muxed_pin_register(parent_pin, pin->id); } @@ -370,9 +367,7 @@ int dpll_muxed_pin_deregister(struct dpll_pin *parent_pin, struct dpll_pin *pin) mutex_unlock(&parent_pin->lock); if (!ret) { - mutex_lock(&pin->lock); - pin->ref_count--; - mutex_unlock(&pin->lock); + refcount_dec(&pin->ref_count); dpll_notify_muxed_pin_deregister(parent_pin, pin->id); } diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 7ff64df735d93..7db9b23b4a084 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -36,7 +36,7 @@ struct dpll_pin { int id; enum dpll_pin_type type; - int ref_count; + refcount_t ref_count; struct dpll_pin_ops *ops; struct mutex lock; void *priv; diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 5c43da0b4d7dd..6b081d19cf0f3 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -133,8 +133,8 @@ static int __dpll_cmd_dump_sources(struct dpll_device *dpll, } ret = 0; } - if (pin->ops->get_prio) { - prio = pin->ops->get_prio(pin, dpll); + if (dpll->ops->get_prio) { + prio = dpll->ops->get_prio(dpll, pin); if (nla_put_u32(msg, DPLLA_SOURCE_PIN_PRIO, prio)) { nla_nest_cancel(msg, src_attr); ret = -EMSGSIZE; @@ -393,10 +393,10 @@ static int dpll_genl_cmd_set_source_prio(struct sk_buff *skb, struct genl_info * mutex_lock(&dpll->lock); pin = dpll_pin_get_by_id(dpll, src_id); - if (!pin || !pin->ops || !pin->ops->set_prio) + if (!dpll->ops || !dpll->ops->set_prio) ret = -EOPNOTSUPP; else - ret = pin->ops->set_prio(pin, dpll, prio); + ret = dpll->ops->set_prio(dpll, pin, prio); mutex_unlock(&dpll->lock); if (!ret) diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index 39c1a1be222d7..523bd998c3b7e 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -361,13 +361,13 @@ static int ice_synce_get_src_select_supported(struct dpll_device *dpll, int mode /** * ice_synce_get_source_prio * @dpll: registered dpll pointer - * @id: source index + * @pin: dpll pin object * * dpll subsystem callback. * Get source priority value. * Return: source priority value */ -static int ice_synce_get_source_prio(struct dpll_pin *pin, struct dpll_device *dpll) +static int ice_synce_get_source_prio(struct dpll_device *dpll, struct dpll_pin *pin) { struct ice_pf *pf = dpll_priv(dpll); u8 idx = (u8)pin_id(pin); @@ -389,7 +389,7 @@ static int ice_synce_get_source_prio(struct dpll_pin *pin, struct dpll_device *d /** * ice_synce_set_source_prio * @dpll: registered dpll pointer - * @id: source index + * @pin: dpll pin object * @prio: expected priority value * * dpll subsystem callback. @@ -398,8 +398,8 @@ static int ice_synce_get_source_prio(struct dpll_pin *pin, struct dpll_device *d * * 0 - success * * negative - failure */ -static int ice_synce_set_source_prio(struct dpll_pin *pin, - struct dpll_device *dpll, int prio) +static int ice_synce_set_source_prio(struct dpll_device *dpll, + struct dpll_pin *pin, int prio) { struct ice_pf *pf = dpll_priv(dpll); u8 idx, priov = (u8)prio; @@ -435,8 +435,6 @@ static struct dpll_pin_ops ice_synce_source_ops = { .get_type = ice_synce_get_source_type, .set_type = ice_synce_set_source_type, .is_type_supported = ice_synce_get_source_supported, - .get_prio = ice_synce_get_source_prio, - .set_prio = ice_synce_set_source_prio, }; static struct dpll_pin_ops ice_synce_output_ops = { @@ -450,6 +448,8 @@ static struct dpll_device_ops ice_synce_dpll_ops = { .get_lock_status = ice_synce_get_lock_status, .get_source_select_mode = ice_synce_get_source_select_mode, .get_source_select_mode_supported = ice_synce_get_src_select_supported, + .get_prio = ice_synce_get_source_prio, + .set_prio = ice_synce_set_source_prio, }; /** diff --git a/include/linux/dpll.h b/include/linux/dpll.h index 133999be7f066..1e54f56450a0c 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -22,6 +22,8 @@ struct dpll_device_ops { int (*get_source_supported)(struct dpll_device *dpll, int sma, int type); int (*get_output_type)(struct dpll_device *dpll, int sma); int (*get_output_supported)(struct dpll_device, int sma, int type); + int (*get_prio)(struct dpll_device *dpll, struct dpll_pin *pin); + int (*set_prio)(struct dpll_device *dpll, struct dpll_pin *pin, int prio); }; struct dpll_pin_ops { @@ -30,8 +32,6 @@ struct dpll_pin_ops { int (*set_type)(struct dpll_pin *pin, int type); int (*set_source)(struct dpll_pin *pin, struct dpll_device *dpll, int id); int (*set_flags)(struct dpll_pin *pin, struct dpll_device *dpll, int flags); - int (*get_prio)(struct dpll_pin *pin, struct dpll_device *dpll); - int (*set_prio)(struct dpll_pin *pin, struct dpll_device *dpll, int prio); }; enum dpll_pin_type { From fa413f8bf7478fb77b6e09a58e080302c239ea60 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kubalewski Date: Tue, 18 Oct 2022 08:08:04 +0000 Subject: [PATCH 4/6] ice: fix pin register Previously: - Only one pin structure was initialized. Init function iterated over same address, - Name was not used for pin initialization. Signed-off-by: Arkadiusz Kubalewski --- drivers/net/ethernet/intel/ice/ice_synce.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index 523bd998c3b7e..c55c12396c6d6 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -671,8 +671,8 @@ static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, } for (i = 0; i < count; i++) { - dpll_init_pin(&pins->pin, type, ops, pf, NULL, i); - dpll_pin_register(dpll, pins->pin); + dpll_init_pin(&pins[i].pin, type, ops, pf, pins[i].name, i); + ret = dpll_pin_register(dpll, pins[i].pin); if (ret) { ice_synce_release_pins(dpll, pins, i + 1); } From 6c6830881ccc426ec3082b9db21c1d573a1c5b03 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kubalewski Date: Tue, 18 Oct 2022 13:48:30 +0200 Subject: [PATCH 5/6] dpll: fix dpll_pin_alloc The dpll_init_pin function was added for the same reason as dpll_pin_alloc, allow external module to allocate and init a dpll_pin. Merge two functions. dpll_pin_alloc function was used and fixed, its name and behavior is consistent with the function which allocates dpll, dpll_init_pin was removed as it was redundant. Signed-off-by: Arkadiusz Kubalewski --- drivers/dpll/dpll_core.c | 41 ++++++++++++++-------- drivers/dpll/dpll_core.h | 3 -- drivers/net/ethernet/intel/ice/ice_synce.c | 8 +++-- include/linux/dpll.h | 23 +++--------- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index f56a283ea35de..c51dfe0d31dc1 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -180,25 +180,29 @@ void dpll_device_unregister(struct dpll_device *dpll) } EXPORT_SYMBOL_GPL(dpll_device_unregister); -void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, - struct dpll_pin_ops *ops, void *priv, const char *name, - int id) +struct dpll_pin *dpll_pin_alloc(struct dpll_pin_ops *ops, + enum dpll_pin_type type, + const char *name, void *priv) { - *pin = kmalloc(sizeof(**pin), GFP_KERNEL); + struct dpll_pin *pin = kmalloc(sizeof(struct dpll_pin), GFP_KERNEL); - (*pin)->ops = ops; - (*pin)->type = type; - (*pin)->priv = priv; - (*pin)->id = id; + if (!pin) + return pin; + + pin->ops = ops; + pin->type = type; + pin->priv = priv; refcount_set(&((*pin)->ref_count), 0); if (name) - snprintf((*pin)->name, PIN_NAME_LENGTH, "%s", name); + snprintf(pin->name, PIN_NAME_LENGTH, "%s", name); else - snprintf((*pin)->name, PIN_NAME_LENGTH, "%s%d", - IS_TYPE_SOURCE((*pin)->type) ? "source" : "output", - (*pin)->id); + snprintf(pin->name, PIN_NAME_LENGTH, "%s%d", + IS_TYPE_SOURCE(pin->type) ? "source" : "output", + pin->id); + + return pin; } -EXPORT_SYMBOL_GPL(dpll_init_pin); +EXPORT_SYMBOL_GPL(dpll_pin_alloc); static int pin_register(struct xarray *pins, struct dpll_pin *pin) { @@ -208,13 +212,20 @@ static int pin_register(struct xarray *pins, struct dpll_pin *pin) u32 id; xa_for_each(pins, index, pos) { - if (pos == pin) + if (pos == pin || + !strncmp(pos->name, pin->name, PIN_NAME_LENGTH)) return -EEXIST; } ret = xa_alloc(pins, &id, pin, xa_limit_16b, GFP_KERNEL); - if (!ret) + if (!ret) { + /* Assign pin id only when being registered with first dpll, + * pin id shall be equal on all dplls sharing the pin. + */ + if (!pin->ref_count) + pin->id = id; xa_set_mark(pins, id, PIN_TYPE_TO_MARK(pin->type)); + } return ret; } diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 7db9b23b4a084..2e529f5a23ff8 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -74,7 +74,4 @@ int for_each_dpll_device(int id, int (*cb)(struct dpll_device *, void *), struct dpll_device *dpll_device_get_by_id(int id); struct dpll_device *dpll_device_get_by_name(const char *name); void dpll_device_unregister(struct dpll_device *dpll); -void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, - struct dpll_pin_ops *ops, void *priv, - const char *name, int id); #endif diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index c55c12396c6d6..8af586dbd3592 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -618,7 +618,7 @@ static int ice_synce_init_pins(struct ice_hw *hw, bool input, int num_pins, } /** - * ice_synce_register_pins + * ice_synce_release_pins * @dpll: dpll pointer * @pins: pointer to pins array * @count: no of pins @@ -671,10 +671,14 @@ static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, } for (i = 0; i < count; i++) { - dpll_init_pin(&pins[i].pin, type, ops, pf, pins[i].name, i); + pins[i].pin = dpll_pin_alloc(ops, type, pins[i].name, pf); + if (!pins[i].pin) + return -ENOMEM; + ret = dpll_pin_register(dpll, pins[i].pin); if (ret) { ice_synce_release_pins(dpll, pins, i + 1); + return -ENOSPC; } } diff --git a/include/linux/dpll.h b/include/linux/dpll.h index 1e54f56450a0c..9e91370fe04bf 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -55,8 +55,7 @@ int pin_id(struct dpll_pin *pin); /* * dpll_pin_alloc - allocate memory for a new dpll_pin object * @ops: pointer to pin operations structure - * @id: id given by the owner, used to find pin by id - * @dpll_pin_type: type of a pin being allocated + * @type: type of a pin being allocated * @name: human readable pin name of allocated pin * @priv: private data of a registerer * @@ -64,11 +63,10 @@ int pin_id(struct dpll_pin *pin); * callbacks and private data pointer. * * Returns: - * * pointer to initialized pin - success, - * * ERR_PTR(-ENOMEM) - memory failure, - * * ERR_PTR(-EINVAL) - given pin type is invalid. + * * pointer to initialized pin - success + * * NULL - memory allocation fail **/ -struct dpll_pin *dpll_pin_alloc(struct dpll_pin_ops *ops, int id, +struct dpll_pin *dpll_pin_alloc(struct dpll_pin_ops *ops, enum dpll_pin_type type, const char *name, void *priv); @@ -186,19 +184,6 @@ struct dpll_pin *dpll_pin_get_by_name(struct dpll_device *dpll, const char *name **/ struct dpll_pin *dpll_pin_get_by_id(struct dpll_device *dpll, int id); -/* - * dpll_init_pins - initialize data of dpll pins - * @pin: pointer to pointer to dpll object to initialize - * @type: the type of dpll pin - * @ops: pointer to dpll ops - * @priv: private data of an dpll pin object - * @name: human readable pin name - * @id: dpll pin id - **/ -void dpll_init_pin(struct dpll_pin **pin, enum dpll_pin_type type, - struct dpll_pin_ops *ops, void *priv, const char *name, - int id); - int dpll_notify_status_locked(int dpll_id); int dpll_notify_status_unlocked(int dpll_id); int dpll_notify_source_change(int dpll_id, int pin_id); From d9006e5234c055767ca9e5686d0420fcf1a82d61 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kubalewski Date: Wed, 19 Oct 2022 01:01:29 +0200 Subject: [PATCH 6/6] ice: fix crash when initalizing same name pins Add a new feature flag for SyncE. Fixes: - zero init a pin stracture on allocation - check if memory was allocated - in case of memory allocation failure stop and release resources - remove mutex lock from worker function Signed-off-by: Arkadiusz Kubalewski --- drivers/dpll/dpll_core.c | 8 +-- drivers/net/ethernet/intel/ice/ice.h | 1 + drivers/net/ethernet/intel/ice/ice_synce.c | 79 +++++++++++++++------- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index c51dfe0d31dc1..0edbd90519239 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -184,7 +184,7 @@ struct dpll_pin *dpll_pin_alloc(struct dpll_pin_ops *ops, enum dpll_pin_type type, const char *name, void *priv) { - struct dpll_pin *pin = kmalloc(sizeof(struct dpll_pin), GFP_KERNEL); + struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL); if (!pin) return pin; @@ -255,7 +255,6 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin) if (!ret) { refcount_inc(&pin->ref_count); change_pin_count(dpll, pin, true); - xa_set_mark(&dpll->pins, dpll->id, DPLL_REGISTERED); } mutex_unlock(&dpll->lock); @@ -293,9 +292,10 @@ int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin) mutex_lock(&dpll->lock); ret = pin_deregister(&dpll->pins, pin); - if (!ret) + if (!ret) { change_pin_count(dpll, pin, false); - + pin->ref_count--; + } mutex_unlock(&dpll->lock); if (!ret) { diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index b2802309462e5..b875bef5f52e4 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -489,6 +489,7 @@ enum ice_pf_flags { ICE_FLAG_PLUG_AUX_DEV, ICE_FLAG_MTU_CHANGED, ICE_FLAG_GNSS, /* GNSS successfully initialized */ + ICE_FLAG_SYNCE, /* SyncE is enabled by software */ ICE_PF_FLAGS_NBITS /* must be last */ }; diff --git a/drivers/net/ethernet/intel/ice/ice_synce.c b/drivers/net/ethernet/intel/ice/ice_synce.c index 8af586dbd3592..4d83e07bbd666 100644 --- a/drivers/net/ethernet/intel/ice/ice_synce.c +++ b/drivers/net/ethernet/intel/ice/ice_synce.c @@ -672,8 +672,10 @@ static int ice_synce_register_pins(struct ice_pf *pf, struct dpll_device *dpll, for (i = 0; i < count; i++) { pins[i].pin = dpll_pin_alloc(ops, type, pins[i].name, pf); - if (!pins[i].pin) + if (!pins[i].pin) { + ice_synce_release_pins(dpll, pins, i); return -ENOMEM; + } ret = dpll_pin_register(dpll, pins[i].pin); if (ret) { @@ -806,7 +808,10 @@ static void ice_synce_periodic_work(struct kthread_work *work) struct ice_synce *synce = container_of(work, struct ice_synce, work.work); struct ice_pf *pf = container_of(synce, struct ice_pf, synce); - int ret; + int ret = 0; + + if (!test_bit(ICE_FLAG_SYNCE, pf->flags)) + return; mutex_lock(&pf->synce.lock); ret = ice_synce_update_dpll_state(pf, synce->dpll_state); @@ -827,6 +832,7 @@ static void ice_synce_periodic_work(struct kthread_work *work) } } mutex_unlock(&pf->synce.lock); + /* Run twice a second or reschedule if update failed */ kthread_queue_delayed_work(synce->kworker, &synce->work, ret ? msecs_to_jiffies(10) : @@ -860,6 +866,34 @@ static int ice_synce_init_dpll_worker(struct ice_pf *pf) return 0; } +/** + * __ice_synce_release - Disable the driver/HW support for SyncE and unregister + * the dpll. + * @pf: Board private structure + * + * This function handles the cleanup work required from the initialization by + * freeing resources and unregistering the dpll. + * + * Context: Called under pf->synce.lock + */ +static void __ice_synce_release(struct ice_pf *pf) +{ + struct ice_synce *se = &pf->synce; + + ice_synce_release_info(pf); + if (se->dpll) { + dpll_device_unregister(se->dpll); + dpll_device_free(se->dpll); + dev_dbg(ice_pf_to_dev(pf), "SyncE dpll removed\n"); + } + kthread_cancel_delayed_work_sync(&se->work); + if (se->kworker) { + kthread_destroy_worker(se->kworker); + se->kworker = NULL; + dev_dbg(ice_pf_to_dev(pf), "SyncE worker removed\n"); + } +} + /** * ice_synce_init - Initialize SyncE support * @pf: Board private structure @@ -883,30 +917,35 @@ int ice_synce_init(struct ice_pf *pf) goto unlock; err = ice_synce_init_dpll_worker(pf); if (err) - goto free_info; + goto release; err = ice_synce_init_dpll(pf); if (err) - goto free_info; + goto release; err = ice_synce_register_pins(pf, pf->synce.dpll, pf->synce.inputs, pf->synce.num_inputs, true); if (err) - goto free_info; + goto release; err = ice_synce_register_pins(pf, pf->synce.dpll, pf->synce.outputs, pf->synce.num_outputs, false); if (err) { ice_synce_release_pins(pf->synce.dpll, pf->synce.inputs, pf->synce.num_inputs); - goto free_info; + goto release; } dev_dbg(ice_pf_to_dev(pf), "SyncE init successful\n"); + set_bit(ICE_FLAG_SYNCE, pf->flags); mutex_unlock(&pf->synce.lock); return err; -free_info: - ice_synce_release_info(pf); +release: + dev_warn(ice_pf_to_dev(pf), "SyncE init failure\n"); + __ice_synce_release(pf); unlock: + clear_bit(ICE_FLAG_SYNCE, pf->flags); mutex_unlock(&pf->synce.lock); + mutex_destroy(&pf->synce.lock); + return err; } @@ -916,25 +955,15 @@ int ice_synce_init(struct ice_pf *pf) * @pf: Board private structure * * This function handles the cleanup work required from the initialization by - * clearing out the important information and unregistering the dpll. + * freeing resources and unregistering the dpll. */ void ice_synce_release(struct ice_pf *pf) { - struct ice_synce *se = &pf->synce; - - mutex_lock(&se->lock); - ice_synce_release_info(pf); - if (se->dpll) { - dpll_device_unregister(se->dpll); - dpll_device_free(se->dpll); - dev_dbg(ice_pf_to_dev(pf), "SyncE dpll removed\n"); - } - kthread_cancel_delayed_work_sync(&se->work); - if (se->kworker) { - kthread_destroy_worker(se->kworker); - se->kworker = NULL; - dev_dbg(ice_pf_to_dev(pf), "SyncE worker removed\n"); + if (test_bit(ICE_FLAG_SYNCE, pf->flags)) { + mutex_lock(&pf->synce.lock); + __ice_synce_release(pf); + mutex_unlock(&pf->synce.lock); + mutex_destroy(&pf->synce.lock); + clear_bit(ICE_FLAG_SYNCE, pf->flags); } - mutex_unlock(&se->lock); - mutex_destroy(&se->lock); }