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-10-22 07:33:08 +03:00
# include <linux/io.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>
2020-05-31 21:20:57 +03:00
# include "cadence_master.h"
2017-12-14 08:49:44 +03:00
# 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
2020-05-31 21:21:02 +03:00
static int ctrl_link_mask ;
module_param_named ( sdw_link_mask , ctrl_link_mask , int , 0444 ) ;
2019-08-06 03:55:20 +03:00
MODULE_PARM_DESC ( sdw_link_mask , " Intel link mask (one bit per link) " ) ;
2020-05-31 21:21:02 +03:00
static bool is_link_enabled ( struct fwnode_handle * fw_node , int i )
{
struct fwnode_handle * link ;
char name [ 32 ] ;
u32 quirk_mask = 0 ;
/* Find master handle */
snprintf ( name , sizeof ( name ) ,
" mipi-sdw-link-%d-subproperties " , i ) ;
link = fwnode_get_named_child_node ( fw_node , name ) ;
if ( ! link )
return false ;
fwnode_property_read_u32 ( link ,
" intel-quirk-mask " ,
& quirk_mask ) ;
if ( quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE )
return false ;
return true ;
}
static int sdw_intel_cleanup ( struct sdw_intel_ctx * ctx )
2017-12-14 08:49:44 +03:00
{
2019-12-12 04:45:01 +03:00
struct sdw_intel_link_res * link = ctx - > links ;
2020-05-31 21:21:02 +03:00
u32 link_mask ;
2017-12-14 08:49:44 +03:00
int i ;
if ( ! link )
return 0 ;
2020-05-31 21:21:02 +03:00
link_mask = ctx - > link_mask ;
for ( i = 0 ; i < ctx - > count ; i + + , link + + ) {
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
2017-12-14 08:49:44 +03:00
if ( link - > pdev )
platform_device_unregister ( link - > pdev ) ;
}
return 0 ;
}
2020-05-31 21:21:02 +03:00
static int
sdw_intel_scan_controller ( struct sdw_intel_acpi_info * info )
2017-12-14 08:49:44 +03:00
{
struct acpi_device * adev ;
int ret , i ;
u8 count ;
2020-05-31 21:21:02 +03:00
if ( acpi_bus_get_device ( info - > handle , & adev ) )
return - EINVAL ;
2017-12-14 08:49:44 +03:00
/* 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
2020-05-31 21:21:02 +03:00
/*
* In theory we could check the number of links supported in
* hardware , but in that step we cannot assume SoundWire IP is
* powered .
*
* In addition , if the BIOS doesn ' t even provide this
* ' master - count ' property then all the inits based on link
* masks will fail as well .
*
* We will check the hardware capabilities in the startup ( ) step
*/
2017-12-14 08:49:44 +03:00
if ( ret ) {
dev_err ( & adev - > dev ,
" Failed to read mipi-sdw-master-count: %d \n " , ret ) ;
2020-05-31 21:21:02 +03:00
return - EINVAL ;
2017-12-14 08:49:44 +03:00
}
/* 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 ) ;
2020-05-31 21:21:02 +03:00
return - EINVAL ;
2020-05-08 03:30:46 +03:00
}
if ( ! count ) {
2019-05-22 22:47:31 +03:00
dev_warn ( & adev - > dev , " No SoundWire links detected \n " ) ;
2020-05-31 21:21:02 +03:00
return - EINVAL ;
}
dev_dbg ( & adev - > dev , " ACPI reports %d SDW Link devices \n " , count ) ;
info - > count = count ;
info - > link_mask = 0 ;
for ( i = 0 ; i < count ; i + + ) {
if ( ctrl_link_mask & & ! ( ctrl_link_mask & BIT ( i ) ) ) {
dev_dbg ( & adev - > dev ,
" Link %d masked, will not be enabled \n " , i ) ;
continue ;
}
if ( ! is_link_enabled ( acpi_fwnode_handle ( adev ) , i ) ) {
dev_dbg ( & adev - > dev ,
" Link %d not selected in firmware \n " , i ) ;
continue ;
}
info - > link_mask | = BIT ( i ) ;
2017-12-14 08:49:44 +03:00
}
2020-05-31 21:21:02 +03:00
return 0 ;
}
2020-07-16 18:09:43 +03:00
# define HDA_DSP_REG_ADSPIC2 (0x10)
# define HDA_DSP_REG_ADSPIS2 (0x14)
# define HDA_DSP_REG_ADSPIC2_SNDW BIT(5)
/**
* sdw_intel_enable_irq ( ) - enable / disable Intel SoundWire IRQ
* @ mmio_base : The mmio base of the control register
* @ enable : true if enable
*/
void sdw_intel_enable_irq ( void __iomem * mmio_base , bool enable )
{
u32 val ;
val = readl ( mmio_base + HDA_DSP_REG_ADSPIC2 ) ;
if ( enable )
val | = HDA_DSP_REG_ADSPIC2_SNDW ;
else
val & = ~ HDA_DSP_REG_ADSPIC2_SNDW ;
writel ( val , mmio_base + HDA_DSP_REG_ADSPIC2 ) ;
}
2020-07-16 18:09:44 +03:00
EXPORT_SYMBOL_NS ( sdw_intel_enable_irq , SOUNDWIRE_INTEL_INIT ) ;
2020-07-16 18:09:43 +03:00
2020-05-31 21:21:02 +03:00
static struct sdw_intel_ctx
* sdw_intel_probe_controller ( struct sdw_intel_res * res )
{
struct platform_device_info pdevinfo ;
struct platform_device * pdev ;
struct sdw_intel_link_res * link ;
struct sdw_intel_ctx * ctx ;
struct acpi_device * adev ;
u32 link_mask ;
int count ;
int i ;
if ( ! res )
return NULL ;
if ( acpi_bus_get_device ( res - > handle , & adev ) )
return NULL ;
if ( ! res - > count )
return NULL ;
count = res - > count ;
2017-12-14 08:49:44 +03:00
dev_dbg ( & adev - > dev , " Creating %d SDW Link devices \n " , count ) ;
2020-05-31 21:21:00 +03:00
ctx = devm_kzalloc ( & adev - > dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
2017-12-14 08:49:44 +03:00
if ( ! ctx )
return NULL ;
ctx - > count = count ;
2020-05-31 21:21:00 +03:00
ctx - > links = devm_kcalloc ( & adev - > dev , ctx - > count ,
sizeof ( * ctx - > links ) , GFP_KERNEL ) ;
2017-12-14 08:49:44 +03:00
if ( ! ctx - > links )
2020-05-31 21:21:00 +03:00
return NULL ;
2017-12-14 08:49:44 +03:00
2020-05-31 21:21:02 +03:00
ctx - > count = count ;
ctx - > mmio_base = res - > mmio_base ;
ctx - > link_mask = res - > link_mask ;
ctx - > handle = res - > handle ;
2020-07-16 18:09:40 +03:00
mutex_init ( & ctx - > shim_lock ) ;
2020-05-31 21:21:02 +03:00
2017-12-14 08:49:44 +03:00
link = ctx - > links ;
2020-05-31 21:21:02 +03:00
link_mask = ctx - > link_mask ;
2017-12-14 08:49:44 +03:00
/* Create SDW Master devices */
2020-05-31 21:21:02 +03:00
for ( i = 0 ; i < count ; i + + , link + + ) {
2020-05-31 21:20:59 +03:00
if ( ! ( link_mask & BIT ( i ) ) ) {
2019-08-06 03:55:20 +03:00
dev_dbg ( & adev - > dev ,
" Link %d masked, will not be enabled \n " , i ) ;
continue ;
}
2020-05-31 21:21:02 +03:00
link - > mmio_base = res - > mmio_base ;
2019-12-12 04:45:01 +03:00
link - > registers = res - > mmio_base + SDW_LINK_BASE
2020-05-31 21:21:02 +03:00
+ ( SDW_LINK_SIZE * i ) ;
2019-12-12 04:45:01 +03:00
link - > shim = res - > mmio_base + SDW_SHIM_BASE ;
link - > alh = res - > mmio_base + SDW_ALH_BASE ;
2017-12-14 08:49:44 +03:00
2019-12-12 04:45:01 +03:00
link - > ops = res - > ops ;
2019-12-12 04:45:02 +03:00
link - > dev = res - > dev ;
2018-04-26 16:09:05 +03:00
2020-07-16 18:09:40 +03:00
link - > shim_lock = & ctx - > shim_lock ;
link - > shim_mask = & ctx - > shim_mask ;
2017-12-14 08:49:44 +03:00
memset ( & pdevinfo , 0 , sizeof ( pdevinfo ) ) ;
pdevinfo . parent = res - > parent ;
2020-05-31 21:21:02 +03:00
pdevinfo . name = " intel-sdw " ;
2017-12-14 08:49:44 +03:00
pdevinfo . id = i ;
pdevinfo . fwnode = acpi_fwnode_handle ( adev ) ;
2020-05-31 21:21:01 +03:00
pdevinfo . data = link ;
pdevinfo . size_data = sizeof ( * link ) ;
2017-12-14 08:49:44 +03:00
pdev = platform_device_register_full ( & pdevinfo ) ;
if ( IS_ERR ( pdev ) ) {
dev_err ( & adev - > dev ,
" platform device creation failed: %ld \n " ,
PTR_ERR ( pdev ) ) ;
2020-05-31 21:21:02 +03:00
goto err ;
2017-12-14 08:49:44 +03:00
}
link - > pdev = pdev ;
}
return ctx ;
2020-05-31 21:21:02 +03:00
err :
2020-05-31 21:21:00 +03:00
ctx - > count = i ;
2020-05-31 21:21:02 +03:00
sdw_intel_cleanup ( ctx ) ;
2017-12-14 08:49:44 +03:00
return NULL ;
}
2020-05-31 21:21:02 +03:00
static int
sdw_intel_startup_controller ( struct sdw_intel_ctx * ctx )
{
struct acpi_device * adev ;
struct sdw_intel_link_res * link ;
u32 caps ;
u32 link_mask ;
int i ;
if ( acpi_bus_get_device ( ctx - > handle , & adev ) )
return - EINVAL ;
/* Check SNDWLCAP.LCOUNT */
caps = ioread32 ( ctx - > mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP ) ;
caps & = GENMASK ( 2 , 0 ) ;
/* Check HW supported vs property value */
if ( caps < ctx - > count ) {
dev_err ( & adev - > dev ,
" BIOS master count is larger than hardware capabilities \n " ) ;
return - EINVAL ;
}
if ( ! ctx - > links )
return - EINVAL ;
link = ctx - > links ;
link_mask = ctx - > link_mask ;
/* Startup SDW Master devices */
for ( i = 0 ; i < ctx - > count ; i + + , link + + ) {
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
intel_master_startup ( link - > pdev ) ;
}
return 0 ;
}
2017-12-14 08:49:44 +03:00
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
{
2020-05-31 21:21:02 +03:00
struct sdw_intel_acpi_info * info = cdata ;
2017-12-14 08:49:44 +03:00
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 ;
}
2020-05-31 21:21:02 +03:00
info - > 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
}
/**
2020-05-31 21:21:02 +03:00
* sdw_intel_acpi_scan ( ) - SoundWire Intel init routine
2017-12-14 08:49:44 +03:00
* @ parent_handle : ACPI parent handle
2020-05-31 21:21:02 +03:00
* @ info : description of what firmware / DSDT tables expose
2017-12-14 08:49:44 +03:00
*
2020-05-31 21:21:02 +03:00
* This scans the namespace and queries firmware to figure out which
* links to enable . A follow - up use of sdw_intel_probe ( ) and
* sdw_intel_startup ( ) is required for creation of devices and bus
* startup
2017-12-14 08:49:44 +03:00
*/
2020-05-31 21:21:02 +03:00
int sdw_intel_acpi_scan ( acpi_handle * parent_handle ,
struct sdw_intel_acpi_info * info )
2017-12-14 08:49:44 +03:00
{
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 ,
2020-05-31 21:21:02 +03:00
NULL , info , NULL ) ;
2017-12-14 08:49:44 +03:00
if ( ACPI_FAILURE ( status ) )
2020-05-31 21:21:02 +03:00
return - ENODEV ;
2017-12-14 08:49:44 +03:00
2020-05-31 21:21:02 +03:00
return sdw_intel_scan_controller ( info ) ;
2017-12-14 08:49:44 +03:00
}
2020-07-16 18:09:44 +03:00
EXPORT_SYMBOL_NS ( sdw_intel_acpi_scan , SOUNDWIRE_INTEL_INIT ) ;
2017-12-14 08:49:44 +03:00
2020-05-31 21:21:02 +03:00
/**
* sdw_intel_probe ( ) - SoundWire Intel probe routine
* @ res : resource data
*
* This registers a platform device for each Master handled by the controller ,
* and SoundWire Master and Slave devices will be created by the platform
* device probe . All the information necessary is stored in the context , and
* the res argument pointer can be freed after this step .
* This function will be called after sdw_intel_acpi_scan ( ) by SOF probe .
*/
struct sdw_intel_ctx
* sdw_intel_probe ( struct sdw_intel_res * res )
{
return sdw_intel_probe_controller ( res ) ;
}
2020-07-16 18:09:44 +03:00
EXPORT_SYMBOL_NS ( sdw_intel_probe , SOUNDWIRE_INTEL_INIT ) ;
2020-05-31 21:21:02 +03:00
/**
* sdw_intel_startup ( ) - SoundWire Intel startup
* @ ctx : SoundWire context allocated in the probe
*
* Startup Intel SoundWire controller . This function will be called after
* Intel Audio DSP is powered up .
*/
int sdw_intel_startup ( struct sdw_intel_ctx * ctx )
{
return sdw_intel_startup_controller ( ctx ) ;
}
2020-07-16 18:09:44 +03:00
EXPORT_SYMBOL_NS ( sdw_intel_startup , SOUNDWIRE_INTEL_INIT ) ;
2017-12-14 08:49:44 +03:00
/**
* sdw_intel_exit ( ) - SoundWire Intel exit
2020-05-31 21:21:02 +03:00
* @ ctx : SoundWire context allocated in the probe
2017-12-14 08:49:44 +03:00
*
* Delete the controller instances created and cleanup
*/
2019-12-12 04:45:01 +03:00
void sdw_intel_exit ( struct sdw_intel_ctx * ctx )
2017-12-14 08:49:44 +03:00
{
2020-05-31 21:21:02 +03:00
sdw_intel_cleanup ( ctx ) ;
2017-12-14 08:49:44 +03:00
}
2020-07-16 18:09:44 +03:00
EXPORT_SYMBOL_NS ( sdw_intel_exit , SOUNDWIRE_INTEL_INIT ) ;
2017-12-14 08:49:44 +03:00
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_DESCRIPTION ( " Intel Soundwire Init Library " ) ;