2023-09-13 21:49:37 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* dpll_core . c - DPLL subsystem kernel - space interface implementation .
*
* Copyright ( c ) 2023 Meta Platforms , Inc . and affiliates
* Copyright ( c ) 2023 Intel Corporation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/device.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/string.h>
# include "dpll_core.h"
2023-09-13 21:49:38 +01:00
# include "dpll_netlink.h"
2023-09-13 21:49:37 +01:00
/* Mutex lock to protect DPLL subsystem devices and pins */
DEFINE_MUTEX ( dpll_lock ) ;
DEFINE_XARRAY_FLAGS ( dpll_device_xa , XA_FLAGS_ALLOC ) ;
DEFINE_XARRAY_FLAGS ( dpll_pin_xa , XA_FLAGS_ALLOC ) ;
static u32 dpll_xa_id ;
# define ASSERT_DPLL_REGISTERED(d) \
WARN_ON_ONCE ( ! xa_get_mark ( & dpll_device_xa , ( d ) - > id , DPLL_REGISTERED ) )
# define ASSERT_DPLL_NOT_REGISTERED(d) \
WARN_ON_ONCE ( xa_get_mark ( & dpll_device_xa , ( d ) - > id , DPLL_REGISTERED ) )
# define ASSERT_PIN_REGISTERED(p) \
WARN_ON_ONCE ( ! xa_get_mark ( & dpll_pin_xa , ( p ) - > id , DPLL_REGISTERED ) )
struct dpll_device_registration {
struct list_head list ;
const struct dpll_device_ops * ops ;
void * priv ;
} ;
struct dpll_pin_registration {
struct list_head list ;
const struct dpll_pin_ops * ops ;
void * priv ;
} ;
struct dpll_device * dpll_device_get_by_id ( int id )
{
if ( xa_get_mark ( & dpll_device_xa , id , DPLL_REGISTERED ) )
return xa_load ( & dpll_device_xa , id ) ;
return NULL ;
}
static struct dpll_pin_registration *
dpll_pin_registration_find ( struct dpll_pin_ref * ref ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_registration * reg ;
list_for_each_entry ( reg , & ref - > registration_list , list ) {
if ( reg - > ops = = ops & & reg - > priv = = priv )
return reg ;
}
return NULL ;
}
static int
dpll_xa_ref_pin_add ( struct xarray * xa_pins , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
bool ref_exists = false ;
unsigned long i ;
int ret ;
xa_for_each ( xa_pins , i , ref ) {
if ( ref - > pin ! = pin )
continue ;
reg = dpll_pin_registration_find ( ref , ops , priv ) ;
if ( reg ) {
refcount_inc ( & ref - > refcount ) ;
return 0 ;
}
ref_exists = true ;
break ;
}
if ( ! ref_exists ) {
ref = kzalloc ( sizeof ( * ref ) , GFP_KERNEL ) ;
if ( ! ref )
return - ENOMEM ;
ref - > pin = pin ;
INIT_LIST_HEAD ( & ref - > registration_list ) ;
ret = xa_insert ( xa_pins , pin - > pin_idx , ref , GFP_KERNEL ) ;
if ( ret ) {
kfree ( ref ) ;
return ret ;
}
refcount_set ( & ref - > refcount , 1 ) ;
}
reg = kzalloc ( sizeof ( * reg ) , GFP_KERNEL ) ;
if ( ! reg ) {
if ( ! ref_exists ) {
xa_erase ( xa_pins , pin - > pin_idx ) ;
kfree ( ref ) ;
}
return - ENOMEM ;
}
reg - > ops = ops ;
reg - > priv = priv ;
if ( ref_exists )
refcount_inc ( & ref - > refcount ) ;
list_add_tail ( & reg - > list , & ref - > registration_list ) ;
return 0 ;
}
static int dpll_xa_ref_pin_del ( struct xarray * xa_pins , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
unsigned long i ;
xa_for_each ( xa_pins , i , ref ) {
if ( ref - > pin ! = pin )
continue ;
reg = dpll_pin_registration_find ( ref , ops , priv ) ;
if ( WARN_ON ( ! reg ) )
return - EINVAL ;
if ( refcount_dec_and_test ( & ref - > refcount ) ) {
list_del ( & reg - > list ) ;
kfree ( reg ) ;
xa_erase ( xa_pins , i ) ;
WARN_ON ( ! list_empty ( & ref - > registration_list ) ) ;
kfree ( ref ) ;
}
return 0 ;
}
return - EINVAL ;
}
static int
dpll_xa_ref_dpll_add ( struct xarray * xa_dplls , struct dpll_device * dpll ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
bool ref_exists = false ;
unsigned long i ;
int ret ;
xa_for_each ( xa_dplls , i , ref ) {
if ( ref - > dpll ! = dpll )
continue ;
reg = dpll_pin_registration_find ( ref , ops , priv ) ;
if ( reg ) {
refcount_inc ( & ref - > refcount ) ;
return 0 ;
}
ref_exists = true ;
break ;
}
if ( ! ref_exists ) {
ref = kzalloc ( sizeof ( * ref ) , GFP_KERNEL ) ;
if ( ! ref )
return - ENOMEM ;
ref - > dpll = dpll ;
INIT_LIST_HEAD ( & ref - > registration_list ) ;
ret = xa_insert ( xa_dplls , dpll - > id , ref , GFP_KERNEL ) ;
if ( ret ) {
kfree ( ref ) ;
return ret ;
}
refcount_set ( & ref - > refcount , 1 ) ;
}
reg = kzalloc ( sizeof ( * reg ) , GFP_KERNEL ) ;
if ( ! reg ) {
if ( ! ref_exists ) {
xa_erase ( xa_dplls , dpll - > id ) ;
kfree ( ref ) ;
}
return - ENOMEM ;
}
reg - > ops = ops ;
reg - > priv = priv ;
if ( ref_exists )
refcount_inc ( & ref - > refcount ) ;
list_add_tail ( & reg - > list , & ref - > registration_list ) ;
return 0 ;
}
static void
dpll_xa_ref_dpll_del ( struct xarray * xa_dplls , struct dpll_device * dpll ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
unsigned long i ;
xa_for_each ( xa_dplls , i , ref ) {
if ( ref - > dpll ! = dpll )
continue ;
reg = dpll_pin_registration_find ( ref , ops , priv ) ;
if ( WARN_ON ( ! reg ) )
return ;
if ( refcount_dec_and_test ( & ref - > refcount ) ) {
list_del ( & reg - > list ) ;
kfree ( reg ) ;
xa_erase ( xa_dplls , i ) ;
WARN_ON ( ! list_empty ( & ref - > registration_list ) ) ;
kfree ( ref ) ;
}
return ;
}
}
struct dpll_pin_ref * dpll_xa_ref_dpll_first ( struct xarray * xa_refs )
{
struct dpll_pin_ref * ref ;
unsigned long i = 0 ;
ref = xa_find ( xa_refs , & i , ULONG_MAX , XA_PRESENT ) ;
WARN_ON ( ! ref ) ;
return ref ;
}
static struct dpll_device *
dpll_device_alloc ( const u64 clock_id , u32 device_idx , struct module * module )
{
struct dpll_device * dpll ;
int ret ;
dpll = kzalloc ( sizeof ( * dpll ) , GFP_KERNEL ) ;
if ( ! dpll )
return ERR_PTR ( - ENOMEM ) ;
refcount_set ( & dpll - > refcount , 1 ) ;
INIT_LIST_HEAD ( & dpll - > registration_list ) ;
dpll - > device_idx = device_idx ;
dpll - > clock_id = clock_id ;
dpll - > module = module ;
ret = xa_alloc_cyclic ( & dpll_device_xa , & dpll - > id , dpll , xa_limit_32b ,
& dpll_xa_id , GFP_KERNEL ) ;
if ( ret < 0 ) {
kfree ( dpll ) ;
return ERR_PTR ( ret ) ;
}
xa_init_flags ( & dpll - > pin_refs , XA_FLAGS_ALLOC ) ;
return dpll ;
}
/**
* dpll_device_get - find existing or create new dpll device
* @ clock_id : clock_id of creator
* @ device_idx : idx given by device driver
* @ module : reference to registering module
*
* Get existing object of a dpll device , unique for given arguments .
* Create new if doesn ' t exist yet .
*
* Context : Acquires a lock ( dpll_lock )
* Return :
* * valid dpll_device struct pointer if succeeded
* * ERR_PTR ( X ) - error
*/
struct dpll_device *
dpll_device_get ( u64 clock_id , u32 device_idx , struct module * module )
{
struct dpll_device * dpll , * ret = NULL ;
unsigned long index ;
mutex_lock ( & dpll_lock ) ;
xa_for_each ( & dpll_device_xa , index , dpll ) {
if ( dpll - > clock_id = = clock_id & &
dpll - > device_idx = = device_idx & &
dpll - > module = = module ) {
ret = dpll ;
refcount_inc ( & ret - > refcount ) ;
break ;
}
}
if ( ! ret )
ret = dpll_device_alloc ( clock_id , device_idx , module ) ;
mutex_unlock ( & dpll_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dpll_device_get ) ;
/**
* dpll_device_put - decrease the refcount and free memory if possible
* @ dpll : dpll_device struct pointer
*
* Context : Acquires a lock ( dpll_lock )
* Drop reference for a dpll device , if all references are gone , delete
* dpll device object .
*/
void dpll_device_put ( struct dpll_device * dpll )
{
mutex_lock ( & dpll_lock ) ;
if ( refcount_dec_and_test ( & dpll - > refcount ) ) {
ASSERT_DPLL_NOT_REGISTERED ( dpll ) ;
WARN_ON_ONCE ( ! xa_empty ( & dpll - > pin_refs ) ) ;
xa_destroy ( & dpll - > pin_refs ) ;
xa_erase ( & dpll_device_xa , dpll - > id ) ;
WARN_ON ( ! list_empty ( & dpll - > registration_list ) ) ;
kfree ( dpll ) ;
}
mutex_unlock ( & dpll_lock ) ;
}
EXPORT_SYMBOL_GPL ( dpll_device_put ) ;
static struct dpll_device_registration *
dpll_device_registration_find ( struct dpll_device * dpll ,
const struct dpll_device_ops * ops , void * priv )
{
struct dpll_device_registration * reg ;
list_for_each_entry ( reg , & dpll - > registration_list , list ) {
if ( reg - > ops = = ops & & reg - > priv = = priv )
return reg ;
}
return NULL ;
}
/**
* dpll_device_register - register the dpll device in the subsystem
* @ dpll : pointer to a dpll
* @ type : type of a dpll
* @ ops : ops for a dpll device
* @ priv : pointer to private information of owner
*
* Make dpll device available for user space .
*
* Context : Acquires a lock ( dpll_lock )
* Return :
* * 0 on success
* * negative - error value
*/
int dpll_device_register ( struct dpll_device * dpll , enum dpll_type type ,
const struct dpll_device_ops * ops , void * priv )
{
struct dpll_device_registration * reg ;
bool first_registration = false ;
if ( WARN_ON ( ! ops ) )
return - EINVAL ;
if ( WARN_ON ( ! ops - > mode_get ) )
return - EINVAL ;
if ( WARN_ON ( ! ops - > lock_status_get ) )
return - EINVAL ;
if ( WARN_ON ( type < DPLL_TYPE_PPS | | type > DPLL_TYPE_MAX ) )
return - EINVAL ;
mutex_lock ( & dpll_lock ) ;
reg = dpll_device_registration_find ( dpll , ops , priv ) ;
if ( reg ) {
mutex_unlock ( & dpll_lock ) ;
return - EEXIST ;
}
reg = kzalloc ( sizeof ( * reg ) , GFP_KERNEL ) ;
if ( ! reg ) {
mutex_unlock ( & dpll_lock ) ;
return - ENOMEM ;
}
reg - > ops = ops ;
reg - > priv = priv ;
dpll - > type = type ;
first_registration = list_empty ( & dpll - > registration_list ) ;
list_add_tail ( & reg - > list , & dpll - > registration_list ) ;
if ( ! first_registration ) {
mutex_unlock ( & dpll_lock ) ;
return 0 ;
}
xa_set_mark ( & dpll_device_xa , dpll - > id , DPLL_REGISTERED ) ;
2023-09-13 21:49:38 +01:00
dpll_device_create_ntf ( dpll ) ;
2023-09-13 21:49:37 +01:00
mutex_unlock ( & dpll_lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( dpll_device_register ) ;
/**
* dpll_device_unregister - unregister dpll device
* @ dpll : registered dpll pointer
* @ ops : ops for a dpll device
* @ priv : pointer to private information of owner
*
* Unregister device , make it unavailable for userspace .
* Note : It does not free the memory
* Context : Acquires a lock ( dpll_lock )
*/
void dpll_device_unregister ( struct dpll_device * dpll ,
const struct dpll_device_ops * ops , void * priv )
{
struct dpll_device_registration * reg ;
mutex_lock ( & dpll_lock ) ;
ASSERT_DPLL_REGISTERED ( dpll ) ;
2023-09-13 21:49:38 +01:00
dpll_device_delete_ntf ( dpll ) ;
2023-09-13 21:49:37 +01:00
reg = dpll_device_registration_find ( dpll , ops , priv ) ;
if ( WARN_ON ( ! reg ) ) {
mutex_unlock ( & dpll_lock ) ;
return ;
}
list_del ( & reg - > list ) ;
kfree ( reg ) ;
if ( ! list_empty ( & dpll - > registration_list ) ) {
mutex_unlock ( & dpll_lock ) ;
return ;
}
xa_clear_mark ( & dpll_device_xa , dpll - > id , DPLL_REGISTERED ) ;
mutex_unlock ( & dpll_lock ) ;
}
EXPORT_SYMBOL_GPL ( dpll_device_unregister ) ;
static struct dpll_pin *
dpll_pin_alloc ( u64 clock_id , u32 pin_idx , struct module * module ,
const struct dpll_pin_properties * prop )
{
struct dpll_pin * pin ;
int ret ;
pin = kzalloc ( sizeof ( * pin ) , GFP_KERNEL ) ;
if ( ! pin )
return ERR_PTR ( - ENOMEM ) ;
pin - > pin_idx = pin_idx ;
pin - > clock_id = clock_id ;
pin - > module = module ;
if ( WARN_ON ( prop - > type < DPLL_PIN_TYPE_MUX | |
prop - > type > DPLL_PIN_TYPE_MAX ) ) {
ret = - EINVAL ;
goto err ;
}
pin - > prop = prop ;
refcount_set ( & pin - > refcount , 1 ) ;
xa_init_flags ( & pin - > dpll_refs , XA_FLAGS_ALLOC ) ;
xa_init_flags ( & pin - > parent_refs , XA_FLAGS_ALLOC ) ;
ret = xa_alloc ( & dpll_pin_xa , & pin - > id , pin , xa_limit_16b , GFP_KERNEL ) ;
if ( ret )
goto err ;
return pin ;
err :
xa_destroy ( & pin - > dpll_refs ) ;
xa_destroy ( & pin - > parent_refs ) ;
kfree ( pin ) ;
return ERR_PTR ( ret ) ;
}
/**
* dpll_pin_get - find existing or create new dpll pin
* @ clock_id : clock_id of creator
* @ pin_idx : idx given by dev driver
* @ module : reference to registering module
* @ prop : dpll pin properties
*
* Get existing object of a pin ( unique for given arguments ) or create new
* if doesn ' t exist yet .
*
* Context : Acquires a lock ( dpll_lock )
* Return :
* * valid allocated dpll_pin struct pointer if succeeded
* * ERR_PTR ( X ) - error
*/
struct dpll_pin *
dpll_pin_get ( u64 clock_id , u32 pin_idx , struct module * module ,
const struct dpll_pin_properties * prop )
{
struct dpll_pin * pos , * ret = NULL ;
unsigned long i ;
mutex_lock ( & dpll_lock ) ;
xa_for_each ( & dpll_pin_xa , i , pos ) {
if ( pos - > clock_id = = clock_id & &
pos - > pin_idx = = pin_idx & &
pos - > module = = module ) {
ret = pos ;
refcount_inc ( & ret - > refcount ) ;
break ;
}
}
if ( ! ret )
ret = dpll_pin_alloc ( clock_id , pin_idx , module , prop ) ;
mutex_unlock ( & dpll_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_get ) ;
/**
* dpll_pin_put - decrease the refcount and free memory if possible
* @ pin : pointer to a pin to be put
*
* Drop reference for a pin , if all references are gone , delete pin object .
*
* Context : Acquires a lock ( dpll_lock )
*/
void dpll_pin_put ( struct dpll_pin * pin )
{
mutex_lock ( & dpll_lock ) ;
if ( refcount_dec_and_test ( & pin - > refcount ) ) {
xa_destroy ( & pin - > dpll_refs ) ;
xa_destroy ( & pin - > parent_refs ) ;
xa_erase ( & dpll_pin_xa , pin - > id ) ;
kfree ( pin ) ;
}
mutex_unlock ( & dpll_lock ) ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_put ) ;
static int
__dpll_pin_register ( struct dpll_device * dpll , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
int ret ;
ret = dpll_xa_ref_pin_add ( & dpll - > pin_refs , pin , ops , priv ) ;
if ( ret )
return ret ;
ret = dpll_xa_ref_dpll_add ( & pin - > dpll_refs , dpll , ops , priv ) ;
if ( ret )
goto ref_pin_del ;
xa_set_mark ( & dpll_pin_xa , pin - > id , DPLL_REGISTERED ) ;
2023-09-13 21:49:38 +01:00
dpll_pin_create_ntf ( pin ) ;
2023-09-13 21:49:37 +01:00
return ret ;
ref_pin_del :
dpll_xa_ref_pin_del ( & dpll - > pin_refs , pin , ops , priv ) ;
return ret ;
}
/**
* dpll_pin_register - register the dpll pin in the subsystem
* @ dpll : pointer to a dpll
* @ pin : pointer to a dpll pin
* @ ops : ops for a dpll pin ops
* @ priv : pointer to private information of owner
*
* Context : Acquires a lock ( dpll_lock )
* Return :
* * 0 on success
* * negative - error value
*/
int
dpll_pin_register ( struct dpll_device * dpll , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
int ret ;
if ( WARN_ON ( ! ops ) | |
WARN_ON ( ! ops - > state_on_dpll_get ) | |
WARN_ON ( ! ops - > direction_get ) )
return - EINVAL ;
if ( ASSERT_DPLL_REGISTERED ( dpll ) )
return - EINVAL ;
mutex_lock ( & dpll_lock ) ;
if ( WARN_ON ( ! ( dpll - > module = = pin - > module & &
dpll - > clock_id = = pin - > clock_id ) ) )
ret = - EINVAL ;
else
ret = __dpll_pin_register ( dpll , pin , ops , priv ) ;
mutex_unlock ( & dpll_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_register ) ;
static void
__dpll_pin_unregister ( struct dpll_device * dpll , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
dpll_xa_ref_pin_del ( & dpll - > pin_refs , pin , ops , priv ) ;
dpll_xa_ref_dpll_del ( & pin - > dpll_refs , dpll , ops , priv ) ;
if ( xa_empty ( & pin - > dpll_refs ) )
xa_clear_mark ( & dpll_pin_xa , pin - > id , DPLL_REGISTERED ) ;
}
/**
* dpll_pin_unregister - unregister dpll pin from dpll device
* @ dpll : registered dpll pointer
* @ pin : pointer to a pin
* @ ops : ops for a dpll pin
* @ priv : pointer to private information of owner
*
* Note : It does not free the memory
* Context : Acquires a lock ( dpll_lock )
*/
void dpll_pin_unregister ( struct dpll_device * dpll , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
if ( WARN_ON ( xa_empty ( & dpll - > pin_refs ) ) )
return ;
if ( WARN_ON ( ! xa_empty ( & pin - > parent_refs ) ) )
return ;
mutex_lock ( & dpll_lock ) ;
2023-09-13 21:49:38 +01:00
dpll_pin_delete_ntf ( pin ) ;
2023-09-13 21:49:37 +01:00
__dpll_pin_unregister ( dpll , pin , ops , priv ) ;
mutex_unlock ( & dpll_lock ) ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_unregister ) ;
/**
* dpll_pin_on_pin_register - register a pin with a parent pin
* @ parent : pointer to a parent pin
* @ pin : pointer to a pin
* @ ops : ops for a dpll pin
* @ priv : pointer to private information of owner
*
* Register a pin with a parent pin , create references between them and
* between newly registered pin and dplls connected with a parent pin .
*
* Context : Acquires a lock ( dpll_lock )
* Return :
* * 0 on success
* * negative - error value
*/
int dpll_pin_on_pin_register ( struct dpll_pin * parent , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_ref * ref ;
unsigned long i , stop ;
int ret ;
if ( WARN_ON ( parent - > prop - > type ! = DPLL_PIN_TYPE_MUX ) )
return - EINVAL ;
if ( WARN_ON ( ! ops ) | |
WARN_ON ( ! ops - > state_on_pin_get ) | |
WARN_ON ( ! ops - > direction_get ) )
return - EINVAL ;
if ( ASSERT_PIN_REGISTERED ( parent ) )
return - EINVAL ;
mutex_lock ( & dpll_lock ) ;
ret = dpll_xa_ref_pin_add ( & pin - > parent_refs , parent , ops , priv ) ;
if ( ret )
goto unlock ;
refcount_inc ( & pin - > refcount ) ;
xa_for_each ( & parent - > dpll_refs , i , ref ) {
ret = __dpll_pin_register ( ref - > dpll , pin , ops , priv ) ;
if ( ret ) {
stop = i ;
goto dpll_unregister ;
}
2023-09-13 21:49:38 +01:00
dpll_pin_create_ntf ( pin ) ;
2023-09-13 21:49:37 +01:00
}
mutex_unlock ( & dpll_lock ) ;
return ret ;
dpll_unregister :
xa_for_each ( & parent - > dpll_refs , i , ref )
2023-09-13 21:49:38 +01:00
if ( i < stop ) {
2023-09-13 21:49:37 +01:00
__dpll_pin_unregister ( ref - > dpll , pin , ops , priv ) ;
2023-09-13 21:49:38 +01:00
dpll_pin_delete_ntf ( pin ) ;
}
2023-09-13 21:49:37 +01:00
refcount_dec ( & pin - > refcount ) ;
dpll_xa_ref_pin_del ( & pin - > parent_refs , parent , ops , priv ) ;
unlock :
mutex_unlock ( & dpll_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_on_pin_register ) ;
/**
* dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
* @ parent : pointer to a parent pin
* @ pin : pointer to a pin
* @ ops : ops for a dpll pin
* @ priv : pointer to private information of owner
*
* Context : Acquires a lock ( dpll_lock )
* Note : It does not free the memory
*/
void dpll_pin_on_pin_unregister ( struct dpll_pin * parent , struct dpll_pin * pin ,
const struct dpll_pin_ops * ops , void * priv )
{
struct dpll_pin_ref * ref ;
unsigned long i ;
mutex_lock ( & dpll_lock ) ;
2023-09-13 21:49:38 +01:00
dpll_pin_delete_ntf ( pin ) ;
2023-09-13 21:49:37 +01:00
dpll_xa_ref_pin_del ( & pin - > parent_refs , parent , ops , priv ) ;
refcount_dec ( & pin - > refcount ) ;
xa_for_each ( & pin - > dpll_refs , i , ref )
__dpll_pin_unregister ( ref - > dpll , pin , ops , priv ) ;
mutex_unlock ( & dpll_lock ) ;
}
EXPORT_SYMBOL_GPL ( dpll_pin_on_pin_unregister ) ;
static struct dpll_device_registration *
dpll_device_registration_first ( struct dpll_device * dpll )
{
struct dpll_device_registration * reg ;
reg = list_first_entry_or_null ( ( struct list_head * ) & dpll - > registration_list ,
struct dpll_device_registration , list ) ;
WARN_ON ( ! reg ) ;
return reg ;
}
void * dpll_priv ( struct dpll_device * dpll )
{
struct dpll_device_registration * reg ;
reg = dpll_device_registration_first ( dpll ) ;
return reg - > priv ;
}
const struct dpll_device_ops * dpll_device_ops ( struct dpll_device * dpll )
{
struct dpll_device_registration * reg ;
reg = dpll_device_registration_first ( dpll ) ;
return reg - > ops ;
}
static struct dpll_pin_registration *
dpll_pin_registration_first ( struct dpll_pin_ref * ref )
{
struct dpll_pin_registration * reg ;
reg = list_first_entry_or_null ( & ref - > registration_list ,
struct dpll_pin_registration , list ) ;
WARN_ON ( ! reg ) ;
return reg ;
}
void * dpll_pin_on_dpll_priv ( struct dpll_device * dpll ,
struct dpll_pin * pin )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
ref = xa_load ( & dpll - > pin_refs , pin - > pin_idx ) ;
if ( ! ref )
return NULL ;
reg = dpll_pin_registration_first ( ref ) ;
return reg - > priv ;
}
void * dpll_pin_on_pin_priv ( struct dpll_pin * parent ,
struct dpll_pin * pin )
{
struct dpll_pin_registration * reg ;
struct dpll_pin_ref * ref ;
ref = xa_load ( & pin - > parent_refs , parent - > pin_idx ) ;
if ( ! ref )
return NULL ;
reg = dpll_pin_registration_first ( ref ) ;
return reg - > priv ;
}
const struct dpll_pin_ops * dpll_pin_ops ( struct dpll_pin_ref * ref )
{
struct dpll_pin_registration * reg ;
reg = dpll_pin_registration_first ( ref ) ;
return reg - > ops ;
}
static int __init dpll_init ( void )
{
int ret ;
ret = genl_register_family ( & dpll_nl_family ) ;
if ( ret )
goto error ;
return 0 ;
error :
mutex_destroy ( & dpll_lock ) ;
return ret ;
}
static void __exit dpll_exit ( void )
{
genl_unregister_family ( & dpll_nl_family ) ;
mutex_destroy ( & dpll_lock ) ;
}
subsys_initcall ( dpll_init ) ;
module_exit ( dpll_exit ) ;