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>
2021-05-11 08:21:32 +03:00
# include <linux/auxiliary_bus.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"
2021-05-11 08:21:32 +03:00
static void intel_link_dev_release ( struct device * dev )
{
struct auxiliary_device * auxdev = to_auxiliary_dev ( dev ) ;
struct sdw_intel_link_dev * ldev = auxiliary_dev_to_sdw_intel_link_dev ( auxdev ) ;
kfree ( ldev ) ;
}
/* alloc, init and add link devices */
static struct sdw_intel_link_dev * intel_link_dev_register ( struct sdw_intel_res * res ,
struct sdw_intel_ctx * ctx ,
struct fwnode_handle * fwnode ,
const char * name ,
int link_id )
{
struct sdw_intel_link_dev * ldev ;
struct sdw_intel_link_res * link ;
struct auxiliary_device * auxdev ;
int ret ;
ldev = kzalloc ( sizeof ( * ldev ) , GFP_KERNEL ) ;
if ( ! ldev )
return ERR_PTR ( - ENOMEM ) ;
auxdev = & ldev - > auxdev ;
auxdev - > name = name ;
auxdev - > dev . parent = res - > parent ;
auxdev - > dev . fwnode = fwnode ;
auxdev - > dev . release = intel_link_dev_release ;
/* we don't use an IDA since we already have a link ID */
auxdev - > id = link_id ;
/*
* keep a handle on the allocated memory , to be used in all other functions .
* Since the same pattern is used to skip links that are not enabled , there is
* no need to check if ctx - > ldev [ i ] is NULL later on .
*/
ctx - > ldev [ link_id ] = ldev ;
/* Add link information used in the driver probe */
link = & ldev - > link_res ;
link - > mmio_base = res - > mmio_base ;
link - > registers = res - > mmio_base + SDW_LINK_BASE
+ ( SDW_LINK_SIZE * link_id ) ;
2021-07-23 14:54:51 +03:00
link - > shim = res - > mmio_base + res - > shim_base ;
link - > alh = res - > mmio_base + res - > alh_base ;
2021-05-11 08:21:32 +03:00
link - > ops = res - > ops ;
link - > dev = res - > dev ;
link - > clock_stop_quirks = res - > clock_stop_quirks ;
link - > shim_lock = & ctx - > shim_lock ;
link - > shim_mask = & ctx - > shim_mask ;
link - > link_mask = ctx - > link_mask ;
/* now follow the two-step init/add sequence */
ret = auxiliary_device_init ( auxdev ) ;
if ( ret < 0 ) {
dev_err ( res - > parent , " failed to initialize link dev %s link_id %d \n " ,
name , link_id ) ;
kfree ( ldev ) ;
return ERR_PTR ( ret ) ;
}
ret = auxiliary_device_add ( & ldev - > auxdev ) ;
if ( ret < 0 ) {
dev_err ( res - > parent , " failed to add link dev %s link_id %d \n " ,
ldev - > auxdev . name , link_id ) ;
/* ldev will be freed with the put_device() and .release sequence */
auxiliary_device_uninit ( & ldev - > auxdev ) ;
return ERR_PTR ( ret ) ;
}
return ldev ;
}
static void intel_link_dev_unregister ( struct sdw_intel_link_dev * ldev )
{
auxiliary_device_delete ( & ldev - > auxdev ) ;
auxiliary_device_uninit ( & ldev - > auxdev ) ;
}
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
{
2021-05-11 08:21:32 +03:00
struct sdw_intel_link_dev * ldev ;
2020-05-31 21:21:02 +03:00
u32 link_mask ;
2017-12-14 08:49:44 +03:00
int i ;
2020-05-31 21:21:02 +03:00
link_mask = ctx - > link_mask ;
2021-05-11 08:21:32 +03:00
for ( i = 0 ; i < ctx - > count ; i + + ) {
2020-05-31 21:21:02 +03:00
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
2021-05-11 08:21:32 +03:00
ldev = ctx - > ldev [ i ] ;
2020-08-17 18:29:21 +03:00
2021-05-11 08:21:32 +03:00
pm_runtime_disable ( & ldev - > auxdev . dev ) ;
if ( ! ldev - > link_res . clock_stop_quirks )
pm_runtime_put_noidle ( ldev - > link_res . dev ) ;
intel_link_dev_unregister ( ldev ) ;
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 sdw_intel_link_res * link ;
2021-05-11 08:21:32 +03:00
struct sdw_intel_link_dev * ldev ;
2020-05-31 21:21:02 +03:00
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 ) ;
2021-05-11 08:21:32 +03:00
/*
* we need to alloc / free memory manually and can ' t use devm :
* this routine may be called from a workqueue , and not from
* the parent . probe .
* If devm_ was used , the memory might never be freed on errors .
*/
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
2017-12-14 08:49:44 +03:00
if ( ! ctx )
return NULL ;
ctx - > count = count ;
2021-05-11 08:21:32 +03:00
/*
* allocate the array of pointers . The link - specific data is allocated
* as part of the first loop below and released with the auxiliary_device_uninit ( ) .
* If some links are disabled , the link pointer will remain NULL . Given that the
* number of links is small , this is simpler than using a list to keep track of links .
*/
ctx - > ldev = kcalloc ( ctx - > count , sizeof ( * ctx - > ldev ) , GFP_KERNEL ) ;
if ( ! ctx - > ldev ) {
kfree ( ctx ) ;
2020-05-31 21:21:00 +03:00
return NULL ;
2021-05-11 08:21:32 +03:00
}
2017-12-14 08:49:44 +03:00
2020-05-31 21:21:02 +03:00
ctx - > mmio_base = res - > mmio_base ;
2021-07-23 14:54:51 +03:00
ctx - > shim_base = res - > shim_base ;
ctx - > alh_base = res - > alh_base ;
2020-05-31 21:21:02 +03:00
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
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 ) ;
2021-05-11 08:21:32 +03:00
for ( i = 0 ; i < count ; i + + ) {
if ( ! ( link_mask & BIT ( i ) ) )
2019-08-06 03:55:20 +03:00
continue ;
2021-05-11 08:21:32 +03:00
/*
* init and add a device for each link
*
* The name of the device will be soundwire_intel . link . [ i ] ,
* with the " soundwire_intel " module prefix automatically added
* by the auxiliary bus core .
*/
ldev = intel_link_dev_register ( res ,
ctx ,
acpi_fwnode_handle ( adev ) ,
" link " ,
i ) ;
if ( IS_ERR ( ldev ) )
2020-05-31 21:21:02 +03:00
goto err ;
2021-05-11 08:21:32 +03:00
link = & ldev - > link_res ;
2021-12-22 02:58:50 +03:00
link - > cdns = auxiliary_get_drvdata ( & ldev - > auxdev ) ;
2020-07-16 18:09:45 +03:00
2021-04-06 04:01:01 +03:00
if ( ! link - > cdns ) {
dev_err ( & adev - > dev , " failed to get link->cdns \n " ) ;
/*
* 1 will be subtracted from i in the err label , but we need to call
* intel_link_dev_unregister for this ldev , so plus 1 now
*/
i + + ;
goto err ;
}
2020-07-16 18:09:45 +03:00
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 + + ;
}
2021-05-11 08:21:32 +03:00
ctx - > ids = kcalloc ( num_slaves , sizeof ( * ctx - > ids ) , GFP_KERNEL ) ;
2020-07-16 18:09:47 +03:00
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 :
2021-05-11 08:21:32 +03:00
while ( i - - ) {
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
ldev = ctx - > ldev [ i ] ;
intel_link_dev_unregister ( ldev ) ;
}
kfree ( ctx - > ldev ) ;
kfree ( 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 ;
2021-05-11 08:21:32 +03:00
struct sdw_intel_link_dev * ldev ;
2020-05-31 21:21:02 +03:00
u32 caps ;
u32 link_mask ;
int i ;
if ( acpi_bus_get_device ( ctx - > handle , & adev ) )
return - EINVAL ;
/* Check SNDWLCAP.LCOUNT */
2021-07-23 14:54:51 +03:00
caps = ioread32 ( ctx - > mmio_base + ctx - > shim_base + SDW_SHIM_LCAP ) ;
2020-05-31 21:21:02 +03:00
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 ;
}
2021-05-11 08:21:32 +03:00
if ( ! ctx - > ldev )
2020-05-31 21:21:02 +03:00
return - EINVAL ;
link_mask = ctx - > link_mask ;
/* Startup SDW Master devices */
2021-05-11 08:21:32 +03:00
for ( i = 0 ; i < ctx - > count ; i + + ) {
2020-05-31 21:21:02 +03:00
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
2021-05-11 08:21:32 +03:00
ldev = ctx - > ldev [ i ] ;
intel_link_startup ( & ldev - > auxdev ) ;
2020-08-17 18:29:21 +03:00
2021-05-11 08:21:32 +03:00
if ( ! ldev - > link_res . clock_stop_quirks ) {
2020-08-17 18:29:21 +03:00
/*
* 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 .
*/
2021-05-11 08:21:32 +03:00
pm_runtime_get_noresume ( ldev - > link_res . dev ) ;
2020-08-17 18:29:21 +03:00
}
2020-05-31 21:21:02 +03:00
}
return 0 ;
}
/**
* sdw_intel_probe ( ) - SoundWire Intel probe routine
* @ res : resource data
*
2021-05-11 08:21:32 +03:00
* This registers an auxiliary device for each Master handled by the controller ,
* and SoundWire Master and Slave devices will be created by the auxiliary
2020-05-31 21:21:02 +03:00
* 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 ) ;
2021-05-11 08:21:32 +03:00
kfree ( ctx - > ids ) ;
kfree ( ctx - > ldev ) ;
kfree ( 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 )
{
2021-05-11 08:21:32 +03:00
struct sdw_intel_link_dev * ldev ;
2020-07-16 18:09:46 +03:00
u32 link_mask ;
int i ;
2021-05-11 08:21:32 +03:00
if ( ! ctx - > ldev )
2020-07-16 18:09:46 +03:00
return ;
link_mask = ctx - > link_mask ;
/* Startup SDW Master devices */
2021-05-11 08:21:32 +03:00
for ( i = 0 ; i < ctx - > count ; i + + ) {
2020-07-16 18:09:46 +03:00
if ( ! ( link_mask & BIT ( i ) ) )
continue ;
2021-05-11 08:21:32 +03:00
ldev = ctx - > ldev [ i ] ;
intel_link_process_wakeen_event ( & ldev - > auxdev ) ;
2020-07-16 18:09:46 +03:00
}
}
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 " ) ;