2017-11-07 16:58:41 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-10-02 06:54:12 +04:00
/*
2014-12-13 01:10:17 +03:00
* Greybus bundles
2014-10-02 06:54:12 +04:00
*
2015-04-13 20:48:37 +03:00
* Copyright 2014 - 2015 Google Inc .
* Copyright 2014 - 2015 Linaro Ltd .
2014-10-02 06:54:12 +04:00
*/
2019-08-25 08:54:27 +03:00
# include <linux/greybus.h>
2016-06-03 23:55:36 +03:00
# include "greybus_trace.h"
2014-10-02 06:54:12 +04:00
2015-11-25 17:58:59 +03:00
static ssize_t bundle_class_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2015-04-01 18:02:04 +03:00
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
2015-11-25 17:59:00 +03:00
return sprintf ( buf , " 0x%02x \n " , bundle - > class ) ;
2015-04-01 18:02:04 +03:00
}
2015-11-25 17:58:59 +03:00
static DEVICE_ATTR_RO ( bundle_class ) ;
2015-04-01 18:02:04 +03:00
2015-11-25 17:59:01 +03:00
static ssize_t bundle_id_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
return sprintf ( buf , " %u \n " , bundle - > id ) ;
}
static DEVICE_ATTR_RO ( bundle_id ) ;
2015-04-13 20:48:37 +03:00
static ssize_t state_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
2019-03-19 23:03:08 +03:00
if ( ! bundle - > state )
2015-04-13 20:48:37 +03:00
return sprintf ( buf , " \n " ) ;
return sprintf ( buf , " %s \n " , bundle - > state ) ;
}
static ssize_t state_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t size )
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
kfree ( bundle - > state ) ;
2015-04-17 22:41:47 +03:00
bundle - > state = kstrdup ( buf , GFP_KERNEL ) ;
2015-04-13 20:48:37 +03:00
if ( ! bundle - > state )
return - ENOMEM ;
/* Tell userspace that the file contents changed */
sysfs_notify ( & bundle - > dev . kobj , NULL , " state " ) ;
return size ;
}
static DEVICE_ATTR_RW ( state ) ;
2014-12-13 01:10:17 +03:00
static struct attribute * bundle_attrs [ ] = {
2015-11-25 17:58:59 +03:00
& dev_attr_bundle_class . attr ,
2015-11-25 17:59:01 +03:00
& dev_attr_bundle_id . attr ,
2015-04-13 20:48:37 +03:00
& dev_attr_state . attr ,
2014-10-24 13:34:46 +04:00
NULL ,
} ;
2014-12-13 01:10:17 +03:00
ATTRIBUTE_GROUPS ( bundle ) ;
2014-10-24 13:34:46 +04:00
2015-12-07 17:05:40 +03:00
static struct gb_bundle * gb_bundle_find ( struct gb_interface * intf ,
2019-02-26 03:32:44 +03:00
u8 bundle_id )
2015-12-07 17:05:40 +03:00
{
struct gb_bundle * bundle ;
list_for_each_entry ( bundle , & intf - > bundles , links ) {
if ( bundle - > id = = bundle_id )
return bundle ;
}
return NULL ;
}
2014-12-13 01:10:17 +03:00
static void gb_bundle_release ( struct device * dev )
2014-10-24 13:34:46 +04:00
{
2014-12-13 01:10:17 +03:00
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
2014-10-24 13:34:46 +04:00
2016-06-03 23:55:36 +03:00
trace_gb_bundle_release ( bundle ) ;
2015-04-13 20:48:37 +03:00
kfree ( bundle - > state ) ;
2016-01-21 19:34:09 +03:00
kfree ( bundle - > cport_desc ) ;
2014-12-13 01:10:17 +03:00
kfree ( bundle ) ;
2014-10-24 13:34:46 +04:00
}
2016-09-09 10:47:01 +03:00
# ifdef CONFIG_PM
2016-07-14 23:13:00 +03:00
static void gb_bundle_disable_all_connections ( struct gb_bundle * bundle )
{
struct gb_connection * connection ;
list_for_each_entry ( connection , & bundle - > connections , bundle_links )
gb_connection_disable ( connection ) ;
}
static void gb_bundle_enable_all_connections ( struct gb_bundle * bundle )
{
struct gb_connection * connection ;
list_for_each_entry ( connection , & bundle - > connections , bundle_links )
gb_connection_enable ( connection ) ;
}
static int gb_bundle_suspend ( struct device * dev )
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
const struct dev_pm_ops * pm = dev - > driver - > pm ;
int ret ;
if ( pm & & pm - > runtime_suspend ) {
ret = pm - > runtime_suspend ( & bundle - > dev ) ;
if ( ret )
return ret ;
} else {
gb_bundle_disable_all_connections ( bundle ) ;
}
ret = gb_control_bundle_suspend ( bundle - > intf - > control , bundle - > id ) ;
if ( ret ) {
if ( pm & & pm - > runtime_resume )
ret = pm - > runtime_resume ( dev ) ;
else
gb_bundle_enable_all_connections ( bundle ) ;
return ret ;
}
return 0 ;
}
static int gb_bundle_resume ( struct device * dev )
{
struct gb_bundle * bundle = to_gb_bundle ( dev ) ;
const struct dev_pm_ops * pm = dev - > driver - > pm ;
int ret ;
ret = gb_control_bundle_resume ( bundle - > intf - > control , bundle - > id ) ;
if ( ret )
return ret ;
if ( pm & & pm - > runtime_resume ) {
ret = pm - > runtime_resume ( dev ) ;
if ( ret )
return ret ;
} else {
gb_bundle_enable_all_connections ( bundle ) ;
}
return 0 ;
}
static int gb_bundle_idle ( struct device * dev )
{
pm_runtime_mark_last_busy ( dev ) ;
pm_request_autosuspend ( dev ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops gb_bundle_pm_ops = {
SET_RUNTIME_PM_OPS ( gb_bundle_suspend , gb_bundle_resume , gb_bundle_idle )
} ;
2014-12-13 01:10:17 +03:00
struct device_type greybus_bundle_type = {
. name = " greybus_bundle " ,
. release = gb_bundle_release ,
2016-07-14 23:13:00 +03:00
. pm = & gb_bundle_pm_ops ,
2014-10-24 13:34:46 +04:00
} ;
2014-10-02 06:54:12 +04:00
/*
2014-12-13 01:10:17 +03:00
* Create a gb_bundle structure to represent a discovered
* bundle . Returns a pointer to the new bundle or a null
2014-10-02 06:54:12 +04:00
* pointer if a failure occurs due to memory exhaustion .
*/
2015-04-01 18:02:00 +03:00
struct gb_bundle * gb_bundle_create ( struct gb_interface * intf , u8 bundle_id ,
2015-04-06 13:19:36 +03:00
u8 class )
2014-10-02 06:54:12 +04:00
{
2014-12-13 01:10:17 +03:00
struct gb_bundle * bundle ;
2014-10-02 06:54:12 +04:00
2016-06-03 23:55:30 +03:00
if ( bundle_id = = BUNDLE_ID_NONE ) {
dev_err ( & intf - > dev , " can't use bundle id %u \n " , bundle_id ) ;
return NULL ;
}
2015-06-10 01:42:57 +03:00
/*
* Reject any attempt to reuse a bundle id . We initialize
* these serially , so there ' s no need to worry about keeping
* the interface bundle list locked here .
*/
if ( gb_bundle_find ( intf , bundle_id ) ) {
2015-12-07 17:05:41 +03:00
dev_err ( & intf - > dev , " duplicate bundle id %u \n " , bundle_id ) ;
2015-06-10 01:42:57 +03:00
return NULL ;
}
2014-12-13 01:10:17 +03:00
bundle = kzalloc ( sizeof ( * bundle ) , GFP_KERNEL ) ;
if ( ! bundle )
2014-10-02 06:54:12 +04:00
return NULL ;
2014-12-20 01:56:31 +03:00
bundle - > intf = intf ;
2015-04-01 18:02:00 +03:00
bundle - > id = bundle_id ;
2015-04-06 13:19:36 +03:00
bundle - > class = class ;
2014-12-13 01:10:17 +03:00
INIT_LIST_HEAD ( & bundle - > connections ) ;
2014-10-02 06:54:12 +04:00
2014-12-20 01:56:31 +03:00
bundle - > dev . parent = & intf - > dev ;
2014-12-13 01:10:17 +03:00
bundle - > dev . bus = & greybus_bus_type ;
bundle - > dev . type = & greybus_bundle_type ;
bundle - > dev . groups = bundle_groups ;
2016-04-25 19:20:36 +03:00
bundle - > dev . dma_mask = intf - > dev . dma_mask ;
2014-12-13 01:10:17 +03:00
device_initialize ( & bundle - > dev ) ;
2015-11-25 17:59:05 +03:00
dev_set_name ( & bundle - > dev , " %s.%d " , dev_name ( & intf - > dev ) , bundle_id ) ;
2014-12-13 01:10:17 +03:00
2015-06-04 15:46:45 +03:00
list_add ( & bundle - > links , & intf - > bundles ) ;
2014-10-02 06:54:12 +04:00
2016-06-03 23:55:36 +03:00
trace_gb_bundle_create ( bundle ) ;
2014-12-13 01:10:17 +03:00
return bundle ;
2014-10-02 06:54:12 +04:00
}
2015-12-07 17:05:43 +03:00
int gb_bundle_add ( struct gb_bundle * bundle )
{
int ret ;
ret = device_add ( & bundle - > dev ) ;
if ( ret ) {
dev_err ( & bundle - > dev , " failed to register bundle: %d \n " , ret ) ;
return ret ;
}
2016-06-03 23:55:36 +03:00
trace_gb_bundle_add ( bundle ) ;
2015-12-07 17:05:43 +03:00
return 0 ;
}
2014-10-02 06:54:12 +04:00
/*
2014-12-13 01:10:17 +03:00
* Tear down a previously set up bundle .
2014-10-02 06:54:12 +04:00
*/
2015-06-12 18:21:11 +03:00
void gb_bundle_destroy ( struct gb_bundle * bundle )
2014-10-02 06:54:12 +04:00
{
2016-06-03 23:55:36 +03:00
trace_gb_bundle_destroy ( bundle ) ;
2015-12-07 17:05:43 +03:00
if ( device_is_registered ( & bundle - > dev ) )
device_del ( & bundle - > dev ) ;
2014-10-22 06:43:30 +04:00
2015-12-07 17:05:43 +03:00
list_del ( & bundle - > links ) ;
put_device ( & bundle - > dev ) ;
}