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