2017-11-07 16:58:41 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-08-11 11:30:45 +04:00
/*
* Greybus " Core "
*
2015-05-22 20:59:16 +03:00
* Copyright 2014 - 2015 Google Inc .
* Copyright 2014 - 2015 Linaro Ltd .
2014-08-11 11:30:45 +04:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2015-09-18 18:38:45 +03:00
# define CREATE_TRACE_POINTS
2019-08-25 08:54:27 +03:00
# include <linux/greybus.h>
2015-09-18 18:38:45 +03:00
# include "greybus_trace.h"
2014-08-11 11:30:45 +04:00
2016-07-14 23:13:00 +03:00
# define GB_BUNDLE_AUTOSUSPEND_MS 3000
2014-08-11 11:30:45 +04:00
/* Allow greybus to be disabled at boot if needed */
static bool nogreybus ;
# ifdef MODULE
module_param ( nogreybus , bool , 0444 ) ;
# else
2014-10-20 15:15:50 +04:00
core_param ( nogreybus , nogreybus , bool , 0444 ) ;
2014-08-11 11:30:45 +04:00
# endif
int greybus_disabled ( void )
{
return nogreybus ;
}
EXPORT_SYMBOL_GPL ( greybus_disabled ) ;
2016-06-09 14:04:36 +03:00
static bool greybus_match_one_id ( struct gb_bundle * bundle ,
2018-11-09 16:53:31 +03:00
const struct greybus_bundle_id * id )
2015-11-21 12:52:03 +03:00
{
if ( ( id - > match_flags & GREYBUS_ID_MATCH_VENDOR ) & &
2015-11-25 17:58:56 +03:00
( id - > vendor ! = bundle - > intf - > vendor_id ) )
2016-06-09 14:04:36 +03:00
return false ;
2015-11-21 12:52:03 +03:00
if ( ( id - > match_flags & GREYBUS_ID_MATCH_PRODUCT ) & &
2015-11-25 17:58:56 +03:00
( id - > product ! = bundle - > intf - > product_id ) )
2016-06-09 14:04:36 +03:00
return false ;
2015-11-21 12:52:03 +03:00
if ( ( id - > match_flags & GREYBUS_ID_MATCH_CLASS ) & &
( id - > class ! = bundle - > class ) )
2016-06-09 14:04:36 +03:00
return false ;
2015-11-21 12:52:03 +03:00
2016-06-09 14:04:36 +03:00
return true ;
2015-11-21 12:52:03 +03:00
}
static const struct greybus_bundle_id *
greybus_match_id ( struct gb_bundle * bundle , const struct greybus_bundle_id * id )
{
2018-11-09 16:53:24 +03:00
if ( ! id )
2015-11-21 12:52:03 +03:00
return NULL ;
for ( ; id - > vendor | | id - > product | | id - > class | | id - > driver_info ;
id + + ) {
if ( greybus_match_one_id ( bundle , id ) )
return id ;
}
return NULL ;
}
2016-05-12 09:04:05 +03:00
static int greybus_match_device ( struct device * dev , struct device_driver * drv )
2014-08-11 11:30:45 +04:00
{
2014-11-14 14:54:59 +03:00
struct greybus_driver * driver = to_greybus_driver ( drv ) ;
2016-01-08 22:13:41 +03:00
struct gb_bundle * bundle ;
2015-04-01 18:02:04 +03:00
const struct greybus_bundle_id * id ;
2014-08-11 11:30:45 +04:00
2016-01-08 22:13:41 +03:00
if ( ! is_gb_bundle ( dev ) )
return 0 ;
bundle = to_gb_bundle ( dev ) ;
2015-11-21 12:52:03 +03:00
id = greybus_match_id ( bundle , driver - > id_table ) ;
2014-08-11 11:30:45 +04:00
if ( id )
return 1 ;
2014-11-21 08:56:30 +03:00
/* FIXME - Dynamic ids? */
2014-08-11 11:30:45 +04:00
return 0 ;
}
static int greybus_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
2015-12-03 21:18:02 +03:00
struct gb_host_device * hd ;
2016-04-23 19:47:24 +03:00
struct gb_module * module = NULL ;
2014-12-20 01:56:31 +03:00
struct gb_interface * intf = NULL ;
2016-04-13 20:19:02 +03:00
struct gb_control * control = NULL ;
2014-12-13 01:10:17 +03:00
struct gb_bundle * bundle = NULL ;
2015-11-25 17:59:08 +03:00
struct gb_svc * svc = NULL ;
2014-11-15 23:12:16 +03:00
2015-11-25 17:59:02 +03:00
if ( is_gb_host_device ( dev ) ) {
hd = to_gb_host_device ( dev ) ;
2016-04-23 19:47:24 +03:00
} else if ( is_gb_module ( dev ) ) {
module = to_gb_module ( dev ) ;
hd = module - > hd ;
2014-12-22 01:10:26 +03:00
} else if ( is_gb_interface ( dev ) ) {
2014-12-20 01:56:31 +03:00
intf = to_gb_interface ( dev ) ;
2016-04-23 19:47:24 +03:00
module = intf - > module ;
2015-12-03 21:18:02 +03:00
hd = intf - > hd ;
2016-04-13 20:19:02 +03:00
} else if ( is_gb_control ( dev ) ) {
control = to_gb_control ( dev ) ;
intf = control - > intf ;
2016-05-05 02:21:29 +03:00
module = intf - > module ;
2016-04-13 20:19:02 +03:00
hd = intf - > hd ;
2014-12-13 01:10:17 +03:00
} else if ( is_gb_bundle ( dev ) ) {
bundle = to_gb_bundle ( dev ) ;
2014-12-20 01:56:31 +03:00
intf = bundle - > intf ;
2016-04-23 19:47:24 +03:00
module = intf - > module ;
2015-12-03 21:18:02 +03:00
hd = intf - > hd ;
2015-11-25 17:59:08 +03:00
} else if ( is_gb_svc ( dev ) ) {
svc = to_gb_svc ( dev ) ;
2015-12-03 21:18:02 +03:00
hd = svc - > hd ;
2014-11-15 23:12:16 +03:00
} else {
dev_WARN ( dev , " uevent for unknown greybus device \" type \" ! \n " ) ;
return - EINVAL ;
}
2015-12-03 21:18:02 +03:00
if ( add_uevent_var ( env , " BUS=%u " , hd - > bus_id ) )
return - ENOMEM ;
2016-04-23 19:47:24 +03:00
if ( module ) {
if ( add_uevent_var ( env , " MODULE=%u " , module - > module_id ) )
return - ENOMEM ;
}
2015-12-03 21:18:04 +03:00
if ( intf ) {
if ( add_uevent_var ( env , " INTERFACE=%u " , intf - > interface_id ) )
return - ENOMEM ;
2016-02-27 08:54:38 +03:00
if ( add_uevent_var ( env , " GREYBUS_ID=%08x/%08x " ,
intf - > vendor_id , intf - > product_id ) )
2016-01-12 06:24:54 +03:00
return - ENOMEM ;
2015-12-03 21:18:04 +03:00
}
2014-12-13 01:10:17 +03:00
if ( bundle ) {
2014-11-15 23:12:16 +03:00
// FIXME
2014-12-13 01:10:17 +03:00
// add a uevent that can "load" a bundle type
2014-11-15 23:12:16 +03:00
// This is what we need to bind a driver to so use the info
// in gmod here as well
2015-12-04 12:44:24 +03:00
if ( add_uevent_var ( env , " BUNDLE=%u " , bundle - > id ) )
return - ENOMEM ;
2016-01-22 05:13:41 +03:00
if ( add_uevent_var ( env , " BUNDLE_CLASS=%02x " , bundle - > class ) )
return - ENOMEM ;
2014-11-15 23:12:16 +03:00
}
2014-08-11 11:30:45 +04:00
return 0 ;
}
2016-07-13 03:41:21 +03:00
static void greybus_shutdown ( struct device * dev )
{
if ( is_gb_host_device ( dev ) ) {
struct gb_host_device * hd ;
hd = to_gb_host_device ( dev ) ;
gb_hd_shutdown ( hd ) ;
}
}
2014-10-24 13:34:46 +04:00
struct bus_type greybus_bus_type = {
2014-08-11 11:30:45 +04:00
. name = " greybus " ,
2016-05-12 09:04:05 +03:00
. match = greybus_match_device ,
2014-08-11 11:30:45 +04:00
. uevent = greybus_uevent ,
2016-07-13 03:41:21 +03:00
. shutdown = greybus_shutdown ,
2014-08-11 11:30:45 +04:00
} ;
static int greybus_probe ( struct device * dev )
{
struct greybus_driver * driver = to_greybus_driver ( dev - > driver ) ;
2015-04-01 18:02:04 +03:00
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
const struct greybus_bundle_id * id ;
2014-08-11 11:30:45 +04:00
int retval ;
/* match id */
2015-11-21 12:52:03 +03:00
id = greybus_match_id ( bundle , driver - > id_table ) ;
2014-08-11 11:30:45 +04:00
if ( ! id )
return - ENODEV ;
2016-07-14 23:13:00 +03:00
retval = pm_runtime_get_sync ( & bundle - > intf - > dev ) ;
if ( retval < 0 ) {
pm_runtime_put_noidle ( & bundle - > intf - > dev ) ;
return retval ;
}
2016-07-21 19:09:34 +03:00
retval = gb_control_bundle_activate ( bundle - > intf - > control , bundle - > id ) ;
if ( retval ) {
pm_runtime_put ( & bundle - > intf - > dev ) ;
return retval ;
}
2016-07-14 23:13:00 +03:00
2016-07-14 23:13:00 +03:00
/*
* Unbound bundle devices are always deactivated . During probe , the
* Runtime PM is set to enabled and active and the usage count is
* incremented . If the driver supports runtime PM , it should call
* pm_runtime_put ( ) in its probe routine and pm_runtime_get_sync ( )
* in remove routine .
*/
pm_runtime_set_autosuspend_delay ( dev , GB_BUNDLE_AUTOSUSPEND_MS ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_get_noresume ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
2015-04-01 18:02:04 +03:00
retval = driver - > probe ( bundle , id ) ;
2016-01-21 19:34:09 +03:00
if ( retval ) {
/*
* Catch buggy drivers that fail to destroy their connections .
*/
WARN_ON ( ! list_empty ( & bundle - > connections ) ) ;
2016-07-14 23:13:00 +03:00
gb_control_bundle_deactivate ( bundle - > intf - > control , bundle - > id ) ;
2016-07-14 23:13:00 +03:00
pm_runtime_disable ( dev ) ;
pm_runtime_set_suspended ( dev ) ;
pm_runtime_put_noidle ( dev ) ;
pm_runtime_dont_use_autosuspend ( dev ) ;
pm_runtime_put ( & bundle - > intf - > dev ) ;
2014-08-11 11:30:45 +04:00
return retval ;
2016-01-21 19:34:09 +03:00
}
2014-08-11 11:30:45 +04:00
2016-07-14 23:13:00 +03:00
pm_runtime_put ( & bundle - > intf - > dev ) ;
2014-08-11 11:30:45 +04:00
return 0 ;
}
static int greybus_remove ( struct device * dev )
{
struct greybus_driver * driver = to_greybus_driver ( dev - > driver ) ;
2015-04-01 18:02:04 +03:00
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
2016-01-19 14:51:09 +03:00
struct gb_connection * connection ;
2016-07-14 23:13:00 +03:00
int retval ;
retval = pm_runtime_get_sync ( dev ) ;
if ( retval < 0 )
dev_err ( dev , " failed to resume bundle: %d \n " , retval ) ;
2016-01-19 14:51:09 +03:00
2016-06-09 19:42:19 +03:00
/*
* Disable ( non - offloaded ) connections early in case the interface is
* already gone to avoid unceccessary operation timeouts during
* driver disconnect . Otherwise , only disable incoming requests .
*/
2016-01-19 14:51:18 +03:00
list_for_each_entry ( connection , & bundle - > connections , bundle_links ) {
2016-06-09 19:42:19 +03:00
if ( gb_connection_is_offloaded ( connection ) )
continue ;
2016-01-19 14:51:18 +03:00
if ( bundle - > intf - > disconnected )
2016-05-27 18:26:22 +03:00
gb_connection_disable_forced ( connection ) ;
2016-01-19 14:51:18 +03:00
else
gb_connection_disable_rx ( connection ) ;
}
2014-08-11 11:30:45 +04:00
2015-04-01 18:02:04 +03:00
driver - > disconnect ( bundle ) ;
2016-01-19 14:51:10 +03:00
2016-01-21 19:34:09 +03:00
/* Catch buggy drivers that fail to destroy their connections. */
WARN_ON ( ! list_empty ( & bundle - > connections ) ) ;
2016-01-19 14:51:10 +03:00
2016-07-14 23:13:00 +03:00
if ( ! bundle - > intf - > disconnected )
gb_control_bundle_deactivate ( bundle - > intf - > control , bundle - > id ) ;
2016-07-14 23:13:00 +03:00
pm_runtime_put_noidle ( dev ) ;
pm_runtime_disable ( dev ) ;
pm_runtime_set_suspended ( dev ) ;
pm_runtime_dont_use_autosuspend ( dev ) ;
pm_runtime_put_noidle ( dev ) ;
2014-08-11 11:30:45 +04:00
return 0 ;
}
int greybus_register_driver ( struct greybus_driver * driver , struct module * owner ,
2019-02-26 03:32:44 +03:00
const char * mod_name )
2014-08-11 11:30:45 +04:00
{
int retval ;
if ( greybus_disabled ( ) )
return - ENODEV ;
2016-01-08 22:13:42 +03:00
driver - > driver . bus = & greybus_bus_type ;
2014-08-11 11:30:45 +04:00
driver - > driver . name = driver - > name ;
driver - > driver . probe = greybus_probe ;
driver - > driver . remove = greybus_remove ;
driver - > driver . owner = owner ;
driver - > driver . mod_name = mod_name ;
retval = driver_register ( & driver - > driver ) ;
if ( retval )
return retval ;
pr_info ( " registered new driver %s \n " , driver - > name ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( greybus_register_driver ) ;
2015-06-08 20:05:13 +03:00
void greybus_deregister_driver ( struct greybus_driver * driver )
2014-08-11 11:30:45 +04:00
{
driver_unregister ( & driver - > driver ) ;
}
2015-06-08 20:05:13 +03:00
EXPORT_SYMBOL_GPL ( greybus_deregister_driver ) ;
2014-08-11 11:30:45 +04:00
2014-08-31 03:21:03 +04:00
static int __init gb_init ( void )
2014-08-31 03:20:22 +04:00
{
2014-08-31 03:47:26 +04:00
int retval ;
2015-03-20 17:59:13 +03:00
if ( greybus_disabled ( ) )
return - ENODEV ;
2015-06-13 19:02:09 +03:00
BUILD_BUG_ON ( CPORT_ID_MAX > = ( long ) CPORT_ID_BAD ) ;
2014-10-02 21:30:03 +04:00
2015-03-27 13:38:06 +03:00
gb_debugfs_init ( ) ;
2014-08-31 03:47:26 +04:00
2014-09-01 00:54:59 +04:00
retval = bus_register ( & greybus_bus_type ) ;
2014-09-14 03:15:52 +04:00
if ( retval ) {
2015-06-10 01:42:51 +03:00
pr_err ( " bus_register failed (%d) \n " , retval ) ;
2014-09-01 00:54:59 +04:00
goto error_bus ;
2014-09-14 03:15:52 +04:00
}
2014-09-01 00:54:59 +04:00
2015-11-25 17:59:02 +03:00
retval = gb_hd_init ( ) ;
if ( retval ) {
pr_err ( " gb_hd_init failed (%d) \n " , retval ) ;
goto error_hd ;
}
2014-10-16 15:35:34 +04:00
retval = gb_operation_init ( ) ;
if ( retval ) {
2015-06-10 01:42:51 +03:00
pr_err ( " gb_operation_init failed (%d) \n " , retval ) ;
2014-10-16 15:35:34 +04:00
goto error_operation ;
}
2014-11-06 01:12:53 +03:00
return 0 ; /* Success */
2014-10-16 15:35:34 +04:00
error_operation :
2015-11-25 17:59:02 +03:00
gb_hd_exit ( ) ;
error_hd :
2014-09-01 00:54:59 +04:00
bus_unregister ( & greybus_bus_type ) ;
error_bus :
2014-09-01 03:17:04 +04:00
gb_debugfs_cleanup ( ) ;
2014-09-01 00:54:59 +04:00
return retval ;
2014-08-31 03:20:22 +04:00
}
2015-03-19 14:32:49 +03:00
module_init ( gb_init ) ;
2014-08-31 03:20:22 +04:00
2014-08-31 03:21:03 +04:00
static void __exit gb_exit ( void )
2014-08-31 03:20:22 +04:00
{
2014-10-16 15:35:34 +04:00
gb_operation_exit ( ) ;
2015-11-25 17:59:02 +03:00
gb_hd_exit ( ) ;
2014-09-01 00:54:59 +04:00
bus_unregister ( & greybus_bus_type ) ;
2014-09-01 03:17:04 +04:00
gb_debugfs_cleanup ( ) ;
2015-09-18 18:38:45 +03:00
tracepoint_synchronize_unregister ( ) ;
2014-08-31 03:20:22 +04:00
}
module_exit ( gb_exit ) ;
2015-04-13 20:51:33 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2014-08-11 11:30:45 +04:00
MODULE_AUTHOR ( " Greg Kroah-Hartman <gregkh@linuxfoundation.org> " ) ;