2017-11-07 16:58:41 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-06-22 14:12:26 +03:00
/*
* Greybus CPort control protocol .
*
* Copyright 2015 Google Inc .
* Copyright 2015 Linaro Ltd .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
2019-08-25 08:54:27 +03:00
# include <linux/greybus.h>
2015-06-22 14:12:26 +03:00
2016-01-19 14:51:13 +03:00
/* Highest control-protocol version supported */
# define GB_CONTROL_VERSION_MAJOR 0
# define GB_CONTROL_VERSION_MINOR 1
2016-01-28 13:20:49 +03:00
static int gb_control_get_version ( struct gb_control * control )
2016-01-19 14:51:14 +03:00
{
struct gb_interface * intf = control - > connection - > intf ;
struct gb_control_version_request request ;
struct gb_control_version_response response ;
int ret ;
request . major = GB_CONTROL_VERSION_MAJOR ;
request . minor = GB_CONTROL_VERSION_MINOR ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_VERSION ,
& request , sizeof ( request ) , & response ,
sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & intf - > dev ,
2018-11-25 19:58:15 +03:00
" failed to get control-protocol version: %d \n " ,
ret ) ;
2016-01-19 14:51:14 +03:00
return ret ;
}
if ( response . major > request . major ) {
dev_err ( & intf - > dev ,
2018-11-25 19:58:15 +03:00
" unsupported major control-protocol version (%u > %u) \n " ,
response . major , request . major ) ;
2016-01-19 14:51:14 +03:00
return - ENOTSUPP ;
}
control - > protocol_major = response . major ;
control - > protocol_minor = response . minor ;
dev_dbg ( & intf - > dev , " %s - %u.%u \n " , __func__ , response . major ,
2018-11-25 19:58:15 +03:00
response . minor ) ;
2016-01-19 14:51:14 +03:00
return 0 ;
}
2016-01-19 14:51:21 +03:00
static int gb_control_get_bundle_version ( struct gb_control * control ,
2018-11-25 19:58:15 +03:00
struct gb_bundle * bundle )
2016-01-19 14:51:21 +03:00
{
struct gb_interface * intf = control - > connection - > intf ;
struct gb_control_bundle_version_request request ;
struct gb_control_bundle_version_response response ;
int ret ;
request . bundle_id = bundle - > id ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_BUNDLE_VERSION ,
& request , sizeof ( request ) ,
& response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & intf - > dev ,
2018-11-25 19:58:15 +03:00
" failed to get bundle %u class version: %d \n " ,
bundle - > id , ret ) ;
2016-01-19 14:51:21 +03:00
return ret ;
}
bundle - > class_major = response . major ;
bundle - > class_minor = response . minor ;
dev_dbg ( & intf - > dev , " %s - %u: %u.%u \n " , __func__ , bundle - > id ,
2018-11-25 19:58:15 +03:00
response . major , response . minor ) ;
2016-01-19 14:51:21 +03:00
return 0 ;
}
int gb_control_get_bundle_versions ( struct gb_control * control )
{
struct gb_interface * intf = control - > connection - > intf ;
struct gb_bundle * bundle ;
int ret ;
if ( ! control - > has_bundle_version )
return 0 ;
list_for_each_entry ( bundle , & intf - > bundles , links ) {
ret = gb_control_get_bundle_version ( control , bundle ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2015-06-22 14:12:26 +03:00
/* Get Manifest's size from the interface */
int gb_control_get_manifest_size_operation ( struct gb_interface * intf )
{
struct gb_control_get_manifest_size_response response ;
struct gb_connection * connection = intf - > control - > connection ;
int ret ;
ret = gb_operation_sync ( connection , GB_CONTROL_TYPE_GET_MANIFEST_SIZE ,
NULL , 0 , & response , sizeof ( response ) ) ;
if ( ret ) {
2015-11-25 17:59:23 +03:00
dev_err ( & connection - > intf - > dev ,
2018-11-25 19:58:15 +03:00
" failed to get manifest size: %d \n " , ret ) ;
2015-06-22 14:12:26 +03:00
return ret ;
}
return le16_to_cpu ( response . size ) ;
}
/* Reads Manifest from the interface */
int gb_control_get_manifest_operation ( struct gb_interface * intf , void * manifest ,
size_t size )
{
struct gb_connection * connection = intf - > control - > connection ;
return gb_operation_sync ( connection , GB_CONTROL_TYPE_GET_MANIFEST ,
NULL , 0 , manifest , size ) ;
}
int gb_control_connected_operation ( struct gb_control * control , u16 cport_id )
{
struct gb_control_connected_request request ;
request . cport_id = cpu_to_le16 ( cport_id ) ;
return gb_operation_sync ( control - > connection , GB_CONTROL_TYPE_CONNECTED ,
& request , sizeof ( request ) , NULL , 0 ) ;
}
int gb_control_disconnected_operation ( struct gb_control * control , u16 cport_id )
{
struct gb_control_disconnected_request request ;
request . cport_id = cpu_to_le16 ( cport_id ) ;
return gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_DISCONNECTED , & request ,
sizeof ( request ) , NULL , 0 ) ;
}
2016-05-27 18:26:26 +03:00
int gb_control_disconnecting_operation ( struct gb_control * control ,
2018-11-25 19:58:15 +03:00
u16 cport_id )
2016-05-27 18:26:26 +03:00
{
2016-08-10 13:58:46 +03:00
struct gb_control_disconnecting_request * request ;
struct gb_operation * operation ;
int ret ;
2016-05-27 18:26:26 +03:00
2016-08-10 13:58:46 +03:00
operation = gb_operation_create_core ( control - > connection ,
2018-11-25 19:58:15 +03:00
GB_CONTROL_TYPE_DISCONNECTING ,
sizeof ( * request ) , 0 , 0 ,
GFP_KERNEL ) ;
2016-08-10 13:58:46 +03:00
if ( ! operation )
return - ENOMEM ;
2016-05-27 18:26:26 +03:00
2016-08-10 13:58:46 +03:00
request = operation - > request - > payload ;
request - > cport_id = cpu_to_le16 ( cport_id ) ;
ret = gb_operation_request_send_sync ( operation ) ;
if ( ret ) {
dev_err ( & control - > dev , " failed to send disconnecting: %d \n " ,
2018-11-25 19:58:15 +03:00
ret ) ;
2016-08-10 13:58:46 +03:00
}
gb_operation_put ( operation ) ;
return ret ;
2016-05-27 18:26:26 +03:00
}
2016-04-29 18:08:34 +03:00
int gb_control_mode_switch_operation ( struct gb_control * control )
{
2016-05-30 10:35:09 +03:00
struct gb_operation * operation ;
2016-05-27 18:26:39 +03:00
int ret ;
2016-05-30 10:35:09 +03:00
operation = gb_operation_create_core ( control - > connection ,
2018-11-25 19:58:15 +03:00
GB_CONTROL_TYPE_MODE_SWITCH ,
0 , 0 ,
GB_OPERATION_FLAG_UNIDIRECTIONAL ,
GFP_KERNEL ) ;
2016-05-30 10:35:09 +03:00
if ( ! operation )
return - ENOMEM ;
ret = gb_operation_request_send_sync ( operation ) ;
2016-08-03 15:09:29 +03:00
if ( ret )
2016-05-30 10:35:09 +03:00
dev_err ( & control - > dev , " failed to send mode switch: %d \n " , ret ) ;
2016-05-27 18:26:39 +03:00
2016-05-30 10:35:09 +03:00
gb_operation_put ( operation ) ;
return ret ;
2016-04-29 18:08:34 +03:00
}
2016-07-08 06:07:00 +03:00
static int gb_control_bundle_pm_status_map ( u8 status )
{
switch ( status ) {
case GB_CONTROL_BUNDLE_PM_INVAL :
return - EINVAL ;
case GB_CONTROL_BUNDLE_PM_BUSY :
return - EBUSY ;
case GB_CONTROL_BUNDLE_PM_NA :
return - ENOMSG ;
case GB_CONTROL_BUNDLE_PM_FAIL :
default :
return - EREMOTEIO ;
}
}
int gb_control_bundle_suspend ( struct gb_control * control , u8 bundle_id )
{
struct gb_control_bundle_pm_request request ;
struct gb_control_bundle_pm_response response ;
int ret ;
request . bundle_id = bundle_id ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_BUNDLE_SUSPEND , & request ,
sizeof ( request ) , & response , sizeof ( response ) ) ;
if ( ret ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to send bundle %u suspend: %d \n " ,
bundle_id , ret ) ;
2016-07-08 06:07:00 +03:00
return ret ;
}
if ( response . status ! = GB_CONTROL_BUNDLE_PM_OK ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to suspend bundle %u: %d \n " ,
bundle_id , response . status ) ;
2016-07-08 06:07:00 +03:00
return gb_control_bundle_pm_status_map ( response . status ) ;
}
return 0 ;
}
int gb_control_bundle_resume ( struct gb_control * control , u8 bundle_id )
{
struct gb_control_bundle_pm_request request ;
struct gb_control_bundle_pm_response response ;
int ret ;
request . bundle_id = bundle_id ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_BUNDLE_RESUME , & request ,
sizeof ( request ) , & response , sizeof ( response ) ) ;
if ( ret ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to send bundle %u resume: %d \n " ,
bundle_id , ret ) ;
2016-07-08 06:07:00 +03:00
return ret ;
}
if ( response . status ! = GB_CONTROL_BUNDLE_PM_OK ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to resume bundle %u: %d \n " ,
bundle_id , response . status ) ;
2016-07-08 06:07:00 +03:00
return gb_control_bundle_pm_status_map ( response . status ) ;
}
return 0 ;
}
2016-07-08 06:07:00 +03:00
int gb_control_bundle_deactivate ( struct gb_control * control , u8 bundle_id )
{
struct gb_control_bundle_pm_request request ;
struct gb_control_bundle_pm_response response ;
int ret ;
request . bundle_id = bundle_id ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_BUNDLE_DEACTIVATE , & request ,
sizeof ( request ) , & response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & control - > dev ,
2016-07-22 08:41:05 +03:00
" failed to send bundle %u deactivate: %d \n " , bundle_id ,
ret ) ;
2016-07-08 06:07:00 +03:00
return ret ;
}
if ( response . status ! = GB_CONTROL_BUNDLE_PM_OK ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to deactivate bundle %u: %d \n " ,
bundle_id , response . status ) ;
2016-07-08 06:07:00 +03:00
return gb_control_bundle_pm_status_map ( response . status ) ;
}
return 0 ;
}
int gb_control_bundle_activate ( struct gb_control * control , u8 bundle_id )
{
struct gb_control_bundle_pm_request request ;
struct gb_control_bundle_pm_response response ;
int ret ;
2016-07-21 15:24:11 +03:00
if ( ! control - > has_bundle_activate )
return 0 ;
2016-07-08 06:07:00 +03:00
request . bundle_id = bundle_id ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_BUNDLE_ACTIVATE , & request ,
sizeof ( request ) , & response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & control - > dev ,
2016-07-22 08:41:05 +03:00
" failed to send bundle %u activate: %d \n " , bundle_id ,
ret ) ;
2016-07-08 06:07:00 +03:00
return ret ;
}
if ( response . status ! = GB_CONTROL_BUNDLE_PM_OK ) {
2016-07-22 08:41:05 +03:00
dev_err ( & control - > dev , " failed to activate bundle %u: %d \n " ,
bundle_id , response . status ) ;
2016-07-08 06:07:00 +03:00
return gb_control_bundle_pm_status_map ( response . status ) ;
}
return 0 ;
}
2016-07-08 06:07:00 +03:00
static int gb_control_interface_pm_status_map ( u8 status )
{
switch ( status ) {
case GB_CONTROL_INTF_PM_BUSY :
return - EBUSY ;
case GB_CONTROL_INTF_PM_NA :
return - ENOMSG ;
default :
return - EREMOTEIO ;
}
}
int gb_control_interface_suspend_prepare ( struct gb_control * control )
{
struct gb_control_intf_pm_response response ;
int ret ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE , NULL , 0 ,
& response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & control - > dev ,
" failed to send interface suspend prepare: %d \n " , ret ) ;
return ret ;
}
if ( response . status ! = GB_CONTROL_INTF_PM_OK ) {
dev_err ( & control - > dev , " interface error while preparing suspend: %d \n " ,
response . status ) ;
return gb_control_interface_pm_status_map ( response . status ) ;
}
return 0 ;
}
2016-07-08 06:07:00 +03:00
int gb_control_interface_deactivate_prepare ( struct gb_control * control )
{
struct gb_control_intf_pm_response response ;
int ret ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE , NULL ,
0 , & response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & control - > dev , " failed to send interface deactivate prepare: %d \n " ,
ret ) ;
return ret ;
}
if ( response . status ! = GB_CONTROL_INTF_PM_OK ) {
dev_err ( & control - > dev , " interface error while preparing deactivate: %d \n " ,
response . status ) ;
return gb_control_interface_pm_status_map ( response . status ) ;
}
return 0 ;
}
2016-07-08 06:07:00 +03:00
int gb_control_interface_hibernate_abort ( struct gb_control * control )
{
struct gb_control_intf_pm_response response ;
int ret ;
ret = gb_operation_sync ( control - > connection ,
GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT , NULL , 0 ,
& response , sizeof ( response ) ) ;
if ( ret ) {
dev_err ( & control - > dev ,
" failed to send interface aborting hibernate: %d \n " ,
ret ) ;
return ret ;
}
if ( response . status ! = GB_CONTROL_INTF_PM_OK ) {
dev_err ( & control - > dev , " interface error while aborting hibernate: %d \n " ,
response . status ) ;
return gb_control_interface_pm_status_map ( response . status ) ;
}
return 0 ;
}
2016-04-13 20:19:04 +03:00
static ssize_t vendor_string_show ( struct device * dev ,
2018-11-25 19:58:15 +03:00
struct device_attribute * attr , char * buf )
2016-04-13 20:19:04 +03:00
{
struct gb_control * control = to_gb_control ( dev ) ;
return scnprintf ( buf , PAGE_SIZE , " %s \n " , control - > vendor_string ) ;
}
static DEVICE_ATTR_RO ( vendor_string ) ;
static ssize_t product_string_show ( struct device * dev ,
2018-11-25 19:58:15 +03:00
struct device_attribute * attr , char * buf )
2016-04-13 20:19:04 +03:00
{
struct gb_control * control = to_gb_control ( dev ) ;
return scnprintf ( buf , PAGE_SIZE , " %s \n " , control - > product_string ) ;
}
static DEVICE_ATTR_RO ( product_string ) ;
static struct attribute * control_attrs [ ] = {
& dev_attr_vendor_string . attr ,
& dev_attr_product_string . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( control ) ;
2016-04-13 20:19:02 +03:00
static void gb_control_release ( struct device * dev )
{
struct gb_control * control = to_gb_control ( dev ) ;
gb_connection_destroy ( control - > connection ) ;
2016-04-13 20:19:04 +03:00
kfree ( control - > vendor_string ) ;
kfree ( control - > product_string ) ;
2016-04-13 20:19:02 +03:00
kfree ( control ) ;
}
struct device_type greybus_control_type = {
. name = " greybus_control " ,
. release = gb_control_release ,
} ;
2015-12-15 17:28:56 +03:00
struct gb_control * gb_control_create ( struct gb_interface * intf )
2015-06-22 14:12:26 +03:00
{
2016-04-13 20:19:05 +03:00
struct gb_connection * connection ;
2015-06-22 14:12:26 +03:00
struct gb_control * control ;
control = kzalloc ( sizeof ( * control ) , GFP_KERNEL ) ;
if ( ! control )
2016-04-13 20:19:05 +03:00
return ERR_PTR ( - ENOMEM ) ;
2015-12-15 17:28:56 +03:00
2016-04-13 20:19:02 +03:00
control - > intf = intf ;
2016-04-13 20:19:05 +03:00
connection = gb_connection_create_control ( intf ) ;
if ( IS_ERR ( connection ) ) {
2016-01-21 19:34:16 +03:00
dev_err ( & intf - > dev ,
2018-11-25 19:58:15 +03:00
" failed to create control connection: %ld \n " ,
PTR_ERR ( connection ) ) ;
2015-12-15 17:28:56 +03:00
kfree ( control ) ;
2016-04-13 20:19:05 +03:00
return ERR_CAST ( connection ) ;
2015-12-15 17:28:56 +03:00
}
2015-06-22 14:12:26 +03:00
2016-04-13 20:19:05 +03:00
control - > connection = connection ;
2016-04-13 20:19:02 +03:00
control - > dev . parent = & intf - > dev ;
control - > dev . bus = & greybus_bus_type ;
control - > dev . type = & greybus_control_type ;
2016-04-13 20:19:04 +03:00
control - > dev . groups = control_groups ;
2016-04-13 20:19:02 +03:00
control - > dev . dma_mask = intf - > dev . dma_mask ;
device_initialize ( & control - > dev ) ;
dev_set_name ( & control - > dev , " %s.ctrl " , dev_name ( & intf - > dev ) ) ;
2016-03-22 21:30:35 +03:00
gb_connection_set_data ( control - > connection , control ) ;
2015-12-15 17:28:56 +03:00
return control ;
}
int gb_control_enable ( struct gb_control * control )
{
int ret ;
2015-06-22 14:12:26 +03:00
2015-12-15 17:28:56 +03:00
dev_dbg ( & control - > connection - > intf - > dev , " %s \n " , __func__ ) ;
2016-01-19 14:51:14 +03:00
ret = gb_connection_enable_tx ( control - > connection ) ;
2015-12-15 17:28:56 +03:00
if ( ret ) {
dev_err ( & control - > connection - > intf - > dev ,
2018-11-25 19:58:15 +03:00
" failed to enable control connection: %d \n " ,
ret ) ;
2015-12-15 17:28:56 +03:00
return ret ;
}
2015-06-22 14:12:26 +03:00
2016-01-19 14:51:14 +03:00
ret = gb_control_get_version ( control ) ;
if ( ret )
goto err_disable_connection ;
2016-01-19 14:51:21 +03:00
if ( control - > protocol_major > 0 | | control - > protocol_minor > 1 )
control - > has_bundle_version = true ;
2016-07-21 15:24:11 +03:00
/* FIXME: use protocol version instead */
2016-07-26 23:41:03 +03:00
if ( ! ( control - > intf - > quirks & GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE ) )
2016-07-21 15:24:11 +03:00
control - > has_bundle_activate = true ;
2015-08-11 05:06:02 +03:00
return 0 ;
2016-01-19 14:51:14 +03:00
err_disable_connection :
gb_connection_disable ( control - > connection ) ;
return ret ;
2015-06-22 14:12:26 +03:00
}
2015-12-15 17:28:56 +03:00
void gb_control_disable ( struct gb_control * control )
2015-06-22 14:12:26 +03:00
{
2015-12-15 17:28:56 +03:00
dev_dbg ( & control - > connection - > intf - > dev , " %s \n " , __func__ ) ;
2015-06-22 14:12:26 +03:00
2016-05-27 18:26:22 +03:00
if ( control - > intf - > disconnected )
gb_connection_disable_forced ( control - > connection ) ;
else
gb_connection_disable ( control - > connection ) ;
2015-12-15 17:28:56 +03:00
}
2016-07-08 06:07:00 +03:00
int gb_control_suspend ( struct gb_control * control )
{
gb_connection_disable ( control - > connection ) ;
return 0 ;
}
int gb_control_resume ( struct gb_control * control )
{
int ret ;
ret = gb_connection_enable_tx ( control - > connection ) ;
if ( ret ) {
dev_err ( & control - > connection - > intf - > dev ,
" failed to enable control connection: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2016-04-13 20:19:03 +03:00
int gb_control_add ( struct gb_control * control )
{
int ret ;
ret = device_add ( & control - > dev ) ;
if ( ret ) {
dev_err ( & control - > dev ,
2018-11-25 19:58:15 +03:00
" failed to register control device: %d \n " ,
ret ) ;
2016-04-13 20:19:03 +03:00
return ret ;
}
return 0 ;
}
void gb_control_del ( struct gb_control * control )
{
if ( device_is_registered ( & control - > dev ) )
device_del ( & control - > dev ) ;
}
2016-05-27 18:26:40 +03:00
struct gb_control * gb_control_get ( struct gb_control * control )
{
get_device ( & control - > dev ) ;
return control ;
}
2016-04-13 20:19:02 +03:00
void gb_control_put ( struct gb_control * control )
2015-12-15 17:28:56 +03:00
{
2016-04-13 20:19:02 +03:00
put_device ( & control - > dev ) ;
2015-06-22 14:12:26 +03:00
}
2016-05-27 18:26:40 +03:00
void gb_control_mode_switch_prepare ( struct gb_control * control )
{
gb_connection_mode_switch_prepare ( control - > connection ) ;
}
void gb_control_mode_switch_complete ( struct gb_control * control )
{
gb_connection_mode_switch_complete ( control - > connection ) ;
}