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>
2020-07-16 18:09:45 +03:00
# include <linux/interrupt.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>
2020-08-17 18:29:12 +03:00
# include <linux/pm_runtime.h>
2017-12-14 08:49:44 +03:00
# 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"
# 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 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 ;
2020-08-17 18:29:12 +03:00
if ( link - > pdev ) {
pm_runtime_disable ( & link - > pdev - > dev ) ;
2017-12-14 08:49:44 +03:00
platform_device_unregister ( link - > pdev ) ;
2020-08-17 18:29:12 +03:00
}
2020-08-17 18:29:21 +03:00
if ( ! link - > clock_stop_quirks )
pm_runtime_put_noidle ( link - > dev ) ;
2017-12-14 08:49:44 +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-07-16 18:09:45 +03:00
irqreturn_t sdw_intel_thread ( int irq , void * dev_id )
{
struct sdw_intel_ctx * ctx = dev_id ;
struct sdw_intel_link_res * link ;
list_for_each_entry ( link , & ctx - > link_list , list )
sdw_cdns_irq ( irq , link - > cdns ) ;
sdw_intel_enable_irq ( ctx - > mmio_base , true ) ;
return IRQ_HANDLED ;
}
EXPORT_SYMBOL_NS ( sdw_intel_thread , SOUNDWIRE_INTEL_INIT ) ;
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 ;
2020-07-16 18:09:47 +03:00
struct sdw_slave * slave ;
struct list_head * node ;
struct sdw_bus * bus ;
2020-05-31 21:21:02 +03:00
u32 link_mask ;
2020-07-16 18:09:47 +03:00
int num_slaves = 0 ;
2020-05-31 21:21:02 +03:00
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
2020-07-16 18:09:45 +03:00
INIT_LIST_HEAD ( & ctx - > link_list ) ;
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-08-17 18:29:18 +03:00
link - > clock_stop_quirks = res - > clock_stop_quirks ;
2020-07-16 18:09:40 +03:00
link - > shim_lock = & ctx - > shim_lock ;
link - > shim_mask = & ctx - > shim_mask ;
2020-09-01 18:05:55 +03:00
link - > link_mask = link_mask ;
2020-07-16 18:09:40 +03:00
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 ;
2020-07-16 18:09:45 +03:00
link - > cdns = platform_get_drvdata ( pdev ) ;
list_add_tail ( & link - > list , & ctx - > link_list ) ;
2020-07-16 18:09:47 +03:00
bus = & link - > cdns - > bus ;
/* Calculate number of slaves */
list_for_each ( node , & bus - > slaves )
num_slaves + + ;
}
ctx - > ids = devm_kcalloc ( & adev - > dev , num_slaves ,
sizeof ( * ctx - > ids ) , GFP_KERNEL ) ;
if ( ! ctx - > ids )
goto err ;
ctx - > num_slaves = num_slaves ;
i = 0 ;
list_for_each_entry ( link , & ctx - > link_list , list ) {
bus = & link - > cdns - > bus ;
list_for_each_entry ( slave , & bus - > slaves , node ) {
ctx - > ids [ i ] . id = slave - > id ;
ctx - > ids [ i ] . link_id = bus - > link_id ;
i + + ;
}
2017-12-14 08:49:44 +03:00
}
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 ) ;
2020-08-17 18:29:21 +03:00
if ( ! link - > clock_stop_quirks ) {
/*
* we need to prevent the parent PCI device
* from entering pm_runtime suspend , so that
* power rails to the SoundWire IP are not
* turned off .
*/
pm_runtime_get_noresume ( link - > dev ) ;
}
2020-05-31 21:21:02 +03:00
}
return 0 ;
}
/**
* 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
2020-07-16 18:09:46 +03:00
void sdw_intel_process_wakeen_event ( struct sdw_intel_ctx * ctx )
{
struct sdw_intel_link_res * link ;
u32 link_mask ;
int i ;
if ( ! ctx - > links )
return ;
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_process_wakeen_event ( link - > pdev ) ;
}
}
EXPORT_SYMBOL_NS ( sdw_intel_process_wakeen_event , SOUNDWIRE_INTEL_INIT ) ;
2017-12-14 08:49:44 +03:00
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_DESCRIPTION ( " Intel Soundwire Init Library " ) ;