2017-12-14 08:49:44 +03:00
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2015-17 Intel Corporation.
/*
* SDW Intel Init Routines
*
* Initializes and creates SDW devices based on ACPI and Hardware values
*/
# include <linux/acpi.h>
2019-04-13 18:12:52 +03:00
# include <linux/export.h>
2019-09-16 21:57:38 +03:00
# include <linux/iomap.h>
2019-04-13 18:12:52 +03:00
# include <linux/module.h>
2017-12-14 08:49:44 +03:00
# include <linux/platform_device.h>
# include <linux/soundwire/sdw_intel.h>
# include "intel.h"
2019-05-22 22:47:17 +03:00
# define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
2017-12-14 08:49:44 +03:00
# define SDW_MAX_LINKS 4
# define SDW_SHIM_LCAP 0x0
# define SDW_SHIM_BASE 0x2C000
# define SDW_ALH_BASE 0x2C800
# define SDW_LINK_BASE 0x30000
# define SDW_LINK_SIZE 0x10000
2019-08-06 03:55:20 +03:00
static int link_mask ;
module_param_named ( sdw_link_mask , link_mask , int , 0444 ) ;
MODULE_PARM_DESC ( sdw_link_mask , " Intel link mask (one bit per link) " ) ;
2017-12-14 08:49:44 +03:00
struct sdw_link_data {
struct sdw_intel_link_res res ;
struct platform_device * pdev ;
} ;
struct sdw_intel_ctx {
int count ;
struct sdw_link_data * links ;
} ;
static int sdw_intel_cleanup_pdev ( struct sdw_intel_ctx * ctx )
{
struct sdw_link_data * link = ctx - > links ;
int i ;
if ( ! link )
return 0 ;
for ( i = 0 ; i < ctx - > count ; i + + ) {
if ( link - > pdev )
platform_device_unregister ( link - > pdev ) ;
link + + ;
}
kfree ( ctx - > links ) ;
ctx - > links = NULL ;
return 0 ;
}
static struct sdw_intel_ctx
* sdw_intel_add_controller ( struct sdw_intel_res * res )
{
struct platform_device_info pdevinfo ;
struct platform_device * pdev ;
struct sdw_link_data * link ;
struct sdw_intel_ctx * ctx ;
struct acpi_device * adev ;
int ret , i ;
u8 count ;
u32 caps ;
if ( acpi_bus_get_device ( res - > handle , & adev ) )
return NULL ;
/* Found controller, find links supported */
count = 0 ;
ret = fwnode_property_read_u8_array ( acpi_fwnode_handle ( adev ) ,
2019-05-01 18:57:37 +03:00
" mipi-sdw-master-count " , & count , 1 ) ;
2017-12-14 08:49:44 +03:00
/* Don't fail on error, continue and use hw value */
if ( ret ) {
dev_err ( & adev - > dev ,
" Failed to read mipi-sdw-master-count: %d \n " , ret ) ;
count = SDW_MAX_LINKS ;
}
/* Check SNDWLCAP.LCOUNT */
caps = ioread32 ( res - > mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP ) ;
2019-05-22 22:47:31 +03:00
caps & = GENMASK ( 2 , 0 ) ;
2017-12-14 08:49:44 +03:00
/* Check HW supported vs property value and use min of two */
count = min_t ( u8 , caps , count ) ;
/* Check count is within bounds */
if ( count > SDW_MAX_LINKS ) {
dev_err ( & adev - > dev , " Link count %d exceeds max %d \n " ,
2019-05-01 18:57:37 +03:00
count , SDW_MAX_LINKS ) ;
2017-12-14 08:49:44 +03:00
return NULL ;
2019-05-22 22:47:31 +03:00
} else if ( ! count ) {
dev_warn ( & adev - > dev , " No SoundWire links detected \n " ) ;
return NULL ;
2017-12-14 08:49:44 +03:00
}
dev_dbg ( & adev - > dev , " Creating %d SDW Link devices \n " , count ) ;
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return NULL ;
ctx - > count = count ;
ctx - > links = kcalloc ( ctx - > count , sizeof ( * ctx - > links ) , GFP_KERNEL ) ;
if ( ! ctx - > links )
goto link_err ;
link = ctx - > links ;
/* Create SDW Master devices */
for ( i = 0 ; i < count ; i + + ) {
2019-08-06 03:55:20 +03:00
if ( link_mask & & ! ( link_mask & BIT ( i ) ) ) {
dev_dbg ( & adev - > dev ,
" Link %d masked, will not be enabled \n " , i ) ;
link + + ;
continue ;
}
2017-12-14 08:49:44 +03:00
link - > res . irq = res - > irq ;
link - > res . registers = res - > mmio_base + SDW_LINK_BASE
+ ( SDW_LINK_SIZE * i ) ;
link - > res . shim = res - > mmio_base + SDW_SHIM_BASE ;
link - > res . alh = res - > mmio_base + SDW_ALH_BASE ;
2018-04-26 16:09:05 +03:00
link - > res . ops = res - > ops ;
link - > res . arg = res - > arg ;
2017-12-14 08:49:44 +03:00
memset ( & pdevinfo , 0 , sizeof ( pdevinfo ) ) ;
pdevinfo . parent = res - > parent ;
pdevinfo . name = " int-sdw " ;
pdevinfo . id = i ;
pdevinfo . fwnode = acpi_fwnode_handle ( adev ) ;
pdevinfo . data = & link - > res ;
pdevinfo . size_data = sizeof ( link - > res ) ;
pdev = platform_device_register_full ( & pdevinfo ) ;
if ( IS_ERR ( pdev ) ) {
dev_err ( & adev - > dev ,
" platform device creation failed: %ld \n " ,
PTR_ERR ( pdev ) ) ;
goto pdev_err ;
}
link - > pdev = pdev ;
link + + ;
}
return ctx ;
pdev_err :
sdw_intel_cleanup_pdev ( ctx ) ;
link_err :
kfree ( ctx ) ;
return NULL ;
}
static acpi_status sdw_intel_acpi_cb ( acpi_handle handle , u32 level ,
2019-05-01 18:57:37 +03:00
void * cdata , void * * return_value )
2017-12-14 08:49:44 +03:00
{
struct sdw_intel_res * res = cdata ;
struct acpi_device * adev ;
2019-05-22 22:47:17 +03:00
acpi_status status ;
u64 adr ;
status = acpi_evaluate_integer ( handle , METHOD_NAME__ADR , NULL , & adr ) ;
if ( ACPI_FAILURE ( status ) )
return AE_OK ; /* keep going */
2017-12-14 08:49:44 +03:00
if ( acpi_bus_get_device ( handle , & adev ) ) {
2018-08-07 09:24:50 +03:00
pr_err ( " %s: Couldn't find ACPI handle \n " , __func__ ) ;
2017-12-14 08:49:44 +03:00
return AE_NOT_FOUND ;
}
res - > handle = handle ;
2019-05-22 22:47:17 +03:00
/*
* On some Intel platforms , multiple children of the HDAS
* device can be found , but only one of them is the SoundWire
* controller . The SNDW device is always exposed with
* Name ( _ADR , 0x40000000 ) , with bits 31. .28 representing the
* SoundWire link so filter accordingly
*/
if ( ( adr & GENMASK ( 31 , 28 ) ) > > 28 ! = SDW_LINK_TYPE )
return AE_OK ; /* keep going */
/* device found, stop namespace walk */
return AE_CTRL_TERMINATE ;
2017-12-14 08:49:44 +03:00
}
/**
* sdw_intel_init ( ) - SoundWire Intel init routine
* @ parent_handle : ACPI parent handle
* @ res : resource data
*
* This scans the namespace and creates SoundWire link controller devices
* based on the info queried .
*/
void * sdw_intel_init ( acpi_handle * parent_handle , struct sdw_intel_res * res )
{
acpi_status status ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE ,
2019-05-01 18:57:37 +03:00
parent_handle , 1 ,
sdw_intel_acpi_cb ,
NULL , res , NULL ) ;
2017-12-14 08:49:44 +03:00
if ( ACPI_FAILURE ( status ) )
return NULL ;
return sdw_intel_add_controller ( res ) ;
}
EXPORT_SYMBOL ( sdw_intel_init ) ;
/**
* sdw_intel_exit ( ) - SoundWire Intel exit
* @ arg : callback context
*
* Delete the controller instances created and cleanup
*/
void sdw_intel_exit ( void * arg )
{
struct sdw_intel_ctx * ctx = arg ;
sdw_intel_cleanup_pdev ( ctx ) ;
kfree ( ctx ) ;
}
EXPORT_SYMBOL ( sdw_intel_exit ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_DESCRIPTION ( " Intel Soundwire Init Library " ) ;