2017-11-07 16:58:41 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-11-03 20:03:22 +03:00
/*
* Greybus Host Device
*
* Copyright 2014 - 2015 Google Inc .
* Copyright 2014 - 2015 Linaro Ltd .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
2019-08-25 08:54:27 +03:00
# include <linux/greybus.h>
2015-11-03 20:03:22 +03:00
2016-05-24 07:05:30 +03:00
# include "greybus_trace.h"
2015-11-03 20:03:22 +03:00
2016-06-09 14:04:40 +03:00
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_hd_create ) ;
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_hd_release ) ;
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_hd_add ) ;
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_hd_del ) ;
2016-06-03 23:55:38 +03:00
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_hd_in ) ;
2016-06-09 14:04:40 +03:00
EXPORT_TRACEPOINT_SYMBOL_GPL ( gb_message_submit ) ;
2016-06-03 23:55:38 +03:00
2015-11-25 17:59:02 +03:00
static struct ida gb_hd_bus_id_map ;
2015-11-03 20:03:22 +03:00
2015-12-23 05:21:51 +03:00
int gb_hd_output ( struct gb_host_device * hd , void * req , u16 size , u8 cmd ,
bool async )
{
if ( ! hd | | ! hd - > driver | | ! hd - > driver - > output )
return - EINVAL ;
return hd - > driver - > output ( hd , req , size , cmd , async ) ;
}
EXPORT_SYMBOL_GPL ( gb_hd_output ) ;
2016-04-29 17:13:23 +03:00
static ssize_t bus_id_show ( struct device * dev ,
2019-08-25 08:54:23 +03:00
struct device_attribute * attr , char * buf )
2016-04-29 17:13:23 +03:00
{
struct gb_host_device * hd = to_gb_host_device ( dev ) ;
return sprintf ( buf , " %d \n " , hd - > bus_id ) ;
}
static DEVICE_ATTR_RO ( bus_id ) ;
static struct attribute * bus_attrs [ ] = {
& dev_attr_bus_id . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( bus ) ;
2016-05-11 11:18:01 +03:00
int gb_hd_cport_reserve ( struct gb_host_device * hd , u16 cport_id )
{
struct ida * id_map = & hd - > cport_id_map ;
int ret ;
ret = ida_simple_get ( id_map , cport_id , cport_id + 1 , GFP_KERNEL ) ;
if ( ret < 0 ) {
dev_err ( & hd - > dev , " failed to reserve cport %u \n " , cport_id ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( gb_hd_cport_reserve ) ;
2016-05-27 08:19:23 +03:00
void gb_hd_cport_release_reserved ( struct gb_host_device * hd , u16 cport_id )
{
struct ida * id_map = & hd - > cport_id_map ;
ida_simple_remove ( id_map , cport_id ) ;
}
EXPORT_SYMBOL_GPL ( gb_hd_cport_release_reserved ) ;
2016-05-11 11:17:59 +03:00
/* Locking: Caller guarantees serialisation */
2016-05-11 11:18:02 +03:00
int gb_hd_cport_allocate ( struct gb_host_device * hd , int cport_id ,
2019-08-25 08:54:23 +03:00
unsigned long flags )
2016-05-11 11:17:59 +03:00
{
struct ida * id_map = & hd - > cport_id_map ;
int ida_start , ida_end ;
2016-05-11 11:18:02 +03:00
if ( hd - > driver - > cport_allocate )
return hd - > driver - > cport_allocate ( hd , cport_id , flags ) ;
2016-05-11 11:17:59 +03:00
if ( cport_id < 0 ) {
ida_start = 0 ;
ida_end = hd - > num_cports ;
} else if ( cport_id < hd - > num_cports ) {
ida_start = cport_id ;
ida_end = cport_id + 1 ;
} else {
dev_err ( & hd - > dev , " cport %d not available \n " , cport_id ) ;
return - EINVAL ;
}
return ida_simple_get ( id_map , ida_start , ida_end , GFP_KERNEL ) ;
}
/* Locking: Caller guarantees serialisation */
void gb_hd_cport_release ( struct gb_host_device * hd , u16 cport_id )
{
2016-05-11 11:18:02 +03:00
if ( hd - > driver - > cport_release ) {
hd - > driver - > cport_release ( hd , cport_id ) ;
return ;
}
2016-05-11 11:17:59 +03:00
ida_simple_remove ( & hd - > cport_id_map , cport_id ) ;
}
2015-11-25 17:59:02 +03:00
static void gb_hd_release ( struct device * dev )
2015-11-03 20:03:22 +03:00
{
2015-11-25 17:59:02 +03:00
struct gb_host_device * hd = to_gb_host_device ( dev ) ;
2015-11-03 20:03:22 +03:00
2016-05-27 19:23:01 +03:00
trace_gb_hd_release ( hd ) ;
2015-12-07 17:05:37 +03:00
if ( hd - > svc )
gb_svc_put ( hd - > svc ) ;
2015-11-25 17:59:02 +03:00
ida_simple_remove ( & gb_hd_bus_id_map , hd - > bus_id ) ;
2015-11-03 20:03:22 +03:00
ida_destroy ( & hd - > cport_id_map ) ;
kfree ( hd ) ;
}
2015-11-25 17:59:02 +03:00
struct device_type greybus_hd_type = {
. name = " greybus_host_device " ,
. release = gb_hd_release ,
} ;
2015-11-03 20:03:25 +03:00
struct gb_host_device * gb_hd_create ( struct gb_hd_driver * driver ,
2019-08-25 08:54:23 +03:00
struct device * parent ,
size_t buffer_size_max ,
size_t num_cports )
2015-11-03 20:03:22 +03:00
{
2015-11-03 20:03:23 +03:00
struct gb_host_device * hd ;
2015-11-25 17:59:02 +03:00
int ret ;
2015-11-03 20:03:22 +03:00
/*
* Validate that the driver implements all of the callbacks
* so that we don ' t have to every time we make them .
*/
if ( ( ! driver - > message_send ) | | ( ! driver - > message_cancel ) ) {
2016-02-11 15:52:47 +03:00
dev_err ( parent , " mandatory hd-callbacks missing \n " ) ;
2015-11-03 20:03:22 +03:00
return ERR_PTR ( - EINVAL ) ;
}
if ( buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN ) {
dev_err ( parent , " greybus host-device buffers too small \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2015-11-22 12:12:58 +03:00
if ( num_cports = = 0 | | num_cports > CPORT_ID_MAX + 1 ) {
2015-11-03 20:03:22 +03:00
dev_err ( parent , " Invalid number of CPorts: %zu \n " , num_cports ) ;
return ERR_PTR ( - EINVAL ) ;
}
/*
* Make sure to never allocate messages larger than what the Greybus
* protocol supports .
*/
if ( buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX ) {
dev_warn ( parent , " limiting buffer size to %u \n " ,
GB_OPERATION_MESSAGE_SIZE_MAX ) ;
buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX ;
}
hd = kzalloc ( sizeof ( * hd ) + driver - > hd_priv_size , GFP_KERNEL ) ;
if ( ! hd )
return ERR_PTR ( - ENOMEM ) ;
2015-11-25 17:59:02 +03:00
ret = ida_simple_get ( & gb_hd_bus_id_map , 1 , 0 , GFP_KERNEL ) ;
if ( ret < 0 ) {
kfree ( hd ) ;
return ERR_PTR ( ret ) ;
}
hd - > bus_id = ret ;
2015-11-03 20:03:22 +03:00
hd - > driver = driver ;
2016-04-23 19:47:24 +03:00
INIT_LIST_HEAD ( & hd - > modules ) ;
2015-11-03 20:03:22 +03:00
INIT_LIST_HEAD ( & hd - > connections ) ;
ida_init ( & hd - > cport_id_map ) ;
hd - > buffer_size_max = buffer_size_max ;
hd - > num_cports = num_cports ;
2015-12-07 17:05:35 +03:00
hd - > dev . parent = parent ;
hd - > dev . bus = & greybus_bus_type ;
hd - > dev . type = & greybus_hd_type ;
2016-04-29 17:13:23 +03:00
hd - > dev . groups = bus_groups ;
2015-12-07 17:05:35 +03:00
hd - > dev . dma_mask = hd - > dev . parent - > dma_mask ;
device_initialize ( & hd - > dev ) ;
dev_set_name ( & hd - > dev , " greybus%d " , hd - > bus_id ) ;
2016-05-24 07:05:30 +03:00
trace_gb_hd_create ( hd ) ;
2015-12-07 17:05:37 +03:00
hd - > svc = gb_svc_create ( hd ) ;
if ( ! hd - > svc ) {
dev_err ( & hd - > dev , " failed to create svc \n " ) ;
2015-12-07 17:05:36 +03:00
put_device ( & hd - > dev ) ;
return ERR_PTR ( - ENOMEM ) ;
2015-11-25 17:59:18 +03:00
}
2015-12-07 17:05:36 +03:00
return hd ;
2015-11-25 17:59:18 +03:00
}
2015-12-07 17:05:36 +03:00
EXPORT_SYMBOL_GPL ( gb_hd_create ) ;
2015-11-25 17:59:18 +03:00
2015-11-04 20:55:22 +03:00
int gb_hd_add ( struct gb_host_device * hd )
{
2015-11-25 17:59:02 +03:00
int ret ;
ret = device_add ( & hd - > dev ) ;
if ( ret )
return ret ;
2015-12-07 17:05:37 +03:00
ret = gb_svc_add ( hd - > svc ) ;
2015-12-07 17:05:34 +03:00
if ( ret ) {
device_del ( & hd - > dev ) ;
return ret ;
}
2016-05-24 07:05:30 +03:00
trace_gb_hd_add ( hd ) ;
2015-11-04 20:55:22 +03:00
return 0 ;
2015-11-03 20:03:22 +03:00
}
2015-11-04 20:55:22 +03:00
EXPORT_SYMBOL_GPL ( gb_hd_add ) ;
2015-11-03 20:03:22 +03:00
2015-11-04 20:55:22 +03:00
void gb_hd_del ( struct gb_host_device * hd )
2015-11-03 20:03:22 +03:00
{
2016-05-24 07:05:30 +03:00
trace_gb_hd_del ( hd ) ;
2016-01-28 14:43:29 +03:00
/*
* Tear down the svc and flush any on - going hotplug processing before
* removing the remaining interfaces .
*/
2015-12-07 17:05:37 +03:00
gb_svc_del ( hd - > svc ) ;
2015-11-25 17:59:02 +03:00
device_del ( & hd - > dev ) ;
2015-11-04 20:55:22 +03:00
}
EXPORT_SYMBOL_GPL ( gb_hd_del ) ;
2015-11-03 20:03:22 +03:00
2016-07-13 03:41:21 +03:00
void gb_hd_shutdown ( struct gb_host_device * hd )
{
gb_svc_del ( hd - > svc ) ;
}
EXPORT_SYMBOL_GPL ( gb_hd_shutdown ) ;
2015-11-04 20:55:22 +03:00
void gb_hd_put ( struct gb_host_device * hd )
{
2015-11-25 17:59:02 +03:00
put_device ( & hd - > dev ) ;
2015-11-03 20:03:22 +03:00
}
2015-11-04 20:55:22 +03:00
EXPORT_SYMBOL_GPL ( gb_hd_put ) ;
2015-11-25 17:59:02 +03:00
int __init gb_hd_init ( void )
{
ida_init ( & gb_hd_bus_id_map ) ;
return 0 ;
}
void gb_hd_exit ( void )
{
ida_destroy ( & gb_hd_bus_id_map ) ;
}