2015-07-27 18:04:03 +03:00
/*
* Intel Sunrisepoint LPSS core support .
*
* Copyright ( C ) 2015 , Intel Corporation
*
* Authors : Andy Shevchenko < andriy . shevchenko @ linux . intel . com >
* Mika Westerberg < mika . westerberg @ linux . intel . com >
* Heikki Krogerus < heikki . krogerus @ linux . intel . com >
* Jarkko Nikula < jarkko . nikula @ linux . intel . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/clk.h>
# include <linux/clkdev.h>
# include <linux/clk-provider.h>
# include <linux/debugfs.h>
# include <linux/idr.h>
# include <linux/ioport.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mfd/core.h>
# include <linux/pm_qos.h>
# include <linux/pm_runtime.h>
2015-11-30 18:11:41 +03:00
# include <linux/property.h>
2015-07-27 18:04:03 +03:00
# include <linux/seq_file.h>
2015-11-07 01:22:15 +03:00
# include <linux/io-64-nonatomic-lo-hi.h>
2015-09-14 11:32:48 +03:00
2015-07-27 18:04:03 +03:00
# include "intel-lpss.h"
# define LPSS_DEV_OFFSET 0x000
# define LPSS_DEV_SIZE 0x200
# define LPSS_PRIV_OFFSET 0x200
# define LPSS_PRIV_SIZE 0x100
2016-04-18 15:14:56 +03:00
# define LPSS_PRIV_REG_COUNT (LPSS_PRIV_SIZE / 4)
2015-07-27 18:04:03 +03:00
# define LPSS_IDMA64_OFFSET 0x800
# define LPSS_IDMA64_SIZE 0x800
/* Offsets from lpss->priv */
# define LPSS_PRIV_RESETS 0x04
# define LPSS_PRIV_RESETS_FUNC BIT(2)
# define LPSS_PRIV_RESETS_IDMA 0x3
# define LPSS_PRIV_ACTIVELTR 0x10
# define LPSS_PRIV_IDLELTR 0x14
# define LPSS_PRIV_LTR_REQ BIT(15)
# define LPSS_PRIV_LTR_SCALE_MASK 0xc00
# define LPSS_PRIV_LTR_SCALE_1US 0x800
# define LPSS_PRIV_LTR_SCALE_32US 0xc00
# define LPSS_PRIV_LTR_VALUE_MASK 0x3ff
# define LPSS_PRIV_SSP_REG 0x20
# define LPSS_PRIV_SSP_REG_DIS_DMA_FIN BIT(0)
2015-09-14 11:32:48 +03:00
# define LPSS_PRIV_REMAP_ADDR 0x40
2015-07-27 18:04:03 +03:00
# define LPSS_PRIV_CAPS 0xfc
# define LPSS_PRIV_CAPS_NO_IDMA BIT(8)
# define LPSS_PRIV_CAPS_TYPE_SHIFT 4
# define LPSS_PRIV_CAPS_TYPE_MASK (0xf << LPSS_PRIV_CAPS_TYPE_SHIFT)
/* This matches the type field in CAPS register */
enum intel_lpss_dev_type {
LPSS_DEV_I2C = 0 ,
LPSS_DEV_UART ,
LPSS_DEV_SPI ,
} ;
struct intel_lpss {
const struct intel_lpss_platform_info * info ;
enum intel_lpss_dev_type type ;
struct clk * clk ;
struct clk_lookup * clock ;
2015-11-30 18:11:41 +03:00
struct mfd_cell * cell ;
2015-07-27 18:04:03 +03:00
struct device * dev ;
void __iomem * priv ;
2016-04-18 15:14:56 +03:00
u32 priv_ctx [ LPSS_PRIV_REG_COUNT ] ;
2015-07-27 18:04:03 +03:00
int devid ;
u32 caps ;
u32 active_ltr ;
u32 idle_ltr ;
struct dentry * debugfs ;
} ;
static const struct resource intel_lpss_dev_resources [ ] = {
DEFINE_RES_MEM_NAMED ( LPSS_DEV_OFFSET , LPSS_DEV_SIZE , " lpss_dev " ) ,
DEFINE_RES_MEM_NAMED ( LPSS_PRIV_OFFSET , LPSS_PRIV_SIZE , " lpss_priv " ) ,
DEFINE_RES_IRQ ( 0 ) ,
} ;
static const struct resource intel_lpss_idma64_resources [ ] = {
DEFINE_RES_MEM ( LPSS_IDMA64_OFFSET , LPSS_IDMA64_SIZE ) ,
DEFINE_RES_IRQ ( 0 ) ,
} ;
# define LPSS_IDMA64_DRIVER_NAME "idma64"
/*
* Cells needs to be ordered so that the iDMA is created first . This is
* because we need to be sure the DMA is available when the host controller
* driver is probed .
*/
static const struct mfd_cell intel_lpss_idma64_cell = {
. name = LPSS_IDMA64_DRIVER_NAME ,
. num_resources = ARRAY_SIZE ( intel_lpss_idma64_resources ) ,
. resources = intel_lpss_idma64_resources ,
} ;
static const struct mfd_cell intel_lpss_i2c_cell = {
. name = " i2c_designware " ,
. num_resources = ARRAY_SIZE ( intel_lpss_dev_resources ) ,
. resources = intel_lpss_dev_resources ,
} ;
static const struct mfd_cell intel_lpss_uart_cell = {
. name = " dw-apb-uart " ,
. num_resources = ARRAY_SIZE ( intel_lpss_dev_resources ) ,
. resources = intel_lpss_dev_resources ,
} ;
static const struct mfd_cell intel_lpss_spi_cell = {
. name = " pxa2xx-spi " ,
. num_resources = ARRAY_SIZE ( intel_lpss_dev_resources ) ,
. resources = intel_lpss_dev_resources ,
} ;
static DEFINE_IDA ( intel_lpss_devid_ida ) ;
static struct dentry * intel_lpss_debugfs ;
static int intel_lpss_request_dma_module ( const char * name )
{
static bool intel_lpss_dma_requested ;
if ( intel_lpss_dma_requested )
return 0 ;
intel_lpss_dma_requested = true ;
return request_module ( " %s " , name ) ;
}
static void intel_lpss_cache_ltr ( struct intel_lpss * lpss )
{
lpss - > active_ltr = readl ( lpss - > priv + LPSS_PRIV_ACTIVELTR ) ;
lpss - > idle_ltr = readl ( lpss - > priv + LPSS_PRIV_IDLELTR ) ;
}
static int intel_lpss_debugfs_add ( struct intel_lpss * lpss )
{
struct dentry * dir ;
dir = debugfs_create_dir ( dev_name ( lpss - > dev ) , intel_lpss_debugfs ) ;
if ( IS_ERR ( dir ) )
return PTR_ERR ( dir ) ;
/* Cache the values into lpss structure */
intel_lpss_cache_ltr ( lpss ) ;
debugfs_create_x32 ( " capabilities " , S_IRUGO , dir , & lpss - > caps ) ;
debugfs_create_x32 ( " active_ltr " , S_IRUGO , dir , & lpss - > active_ltr ) ;
debugfs_create_x32 ( " idle_ltr " , S_IRUGO , dir , & lpss - > idle_ltr ) ;
lpss - > debugfs = dir ;
return 0 ;
}
static void intel_lpss_debugfs_remove ( struct intel_lpss * lpss )
{
debugfs_remove_recursive ( lpss - > debugfs ) ;
}
static void intel_lpss_ltr_set ( struct device * dev , s32 val )
{
struct intel_lpss * lpss = dev_get_drvdata ( dev ) ;
u32 ltr ;
/*
* Program latency tolerance ( LTR ) accordingly what has been asked
* by the PM QoS layer or disable it in case we were passed
* negative value or PM_QOS_LATENCY_ANY .
*/
ltr = readl ( lpss - > priv + LPSS_PRIV_ACTIVELTR ) ;
if ( val = = PM_QOS_LATENCY_ANY | | val < 0 ) {
ltr & = ~ LPSS_PRIV_LTR_REQ ;
} else {
ltr | = LPSS_PRIV_LTR_REQ ;
ltr & = ~ LPSS_PRIV_LTR_SCALE_MASK ;
ltr & = ~ LPSS_PRIV_LTR_VALUE_MASK ;
if ( val > LPSS_PRIV_LTR_VALUE_MASK )
ltr | = LPSS_PRIV_LTR_SCALE_32US | val > > 5 ;
else
ltr | = LPSS_PRIV_LTR_SCALE_1US | val ;
}
if ( ltr = = lpss - > active_ltr )
return ;
writel ( ltr , lpss - > priv + LPSS_PRIV_ACTIVELTR ) ;
writel ( ltr , lpss - > priv + LPSS_PRIV_IDLELTR ) ;
/* Cache the values into lpss structure */
intel_lpss_cache_ltr ( lpss ) ;
}
static void intel_lpss_ltr_expose ( struct intel_lpss * lpss )
{
lpss - > dev - > power . set_latency_tolerance = intel_lpss_ltr_set ;
dev_pm_qos_expose_latency_tolerance ( lpss - > dev ) ;
}
static void intel_lpss_ltr_hide ( struct intel_lpss * lpss )
{
dev_pm_qos_hide_latency_tolerance ( lpss - > dev ) ;
lpss - > dev - > power . set_latency_tolerance = NULL ;
}
static int intel_lpss_assign_devs ( struct intel_lpss * lpss )
{
2015-11-30 18:11:41 +03:00
const struct mfd_cell * cell ;
2015-07-27 18:04:03 +03:00
unsigned int type ;
type = lpss - > caps & LPSS_PRIV_CAPS_TYPE_MASK ;
type > > = LPSS_PRIV_CAPS_TYPE_SHIFT ;
switch ( type ) {
case LPSS_DEV_I2C :
2015-11-30 18:11:41 +03:00
cell = & intel_lpss_i2c_cell ;
2015-07-27 18:04:03 +03:00
break ;
case LPSS_DEV_UART :
2015-11-30 18:11:41 +03:00
cell = & intel_lpss_uart_cell ;
2015-07-27 18:04:03 +03:00
break ;
case LPSS_DEV_SPI :
2015-11-30 18:11:41 +03:00
cell = & intel_lpss_spi_cell ;
2015-07-27 18:04:03 +03:00
break ;
default :
return - ENODEV ;
}
2015-11-30 18:11:41 +03:00
lpss - > cell = devm_kmemdup ( lpss - > dev , cell , sizeof ( * cell ) , GFP_KERNEL ) ;
if ( ! lpss - > cell )
return - ENOMEM ;
2015-07-27 18:04:03 +03:00
lpss - > type = type ;
return 0 ;
}
static bool intel_lpss_has_idma ( const struct intel_lpss * lpss )
{
return ( lpss - > caps & LPSS_PRIV_CAPS_NO_IDMA ) = = 0 ;
}
static void intel_lpss_set_remap_addr ( const struct intel_lpss * lpss )
{
resource_size_t addr = lpss - > info - > mem - > start ;
2015-09-14 11:32:48 +03:00
lo_hi_writeq ( addr , lpss - > priv + LPSS_PRIV_REMAP_ADDR ) ;
2015-07-27 18:04:03 +03:00
}
static void intel_lpss_deassert_reset ( const struct intel_lpss * lpss )
{
u32 value = LPSS_PRIV_RESETS_FUNC | LPSS_PRIV_RESETS_IDMA ;
/* Bring out the device from reset */
writel ( value , lpss - > priv + LPSS_PRIV_RESETS ) ;
}
static void intel_lpss_init_dev ( const struct intel_lpss * lpss )
{
u32 value = LPSS_PRIV_SSP_REG_DIS_DMA_FIN ;
intel_lpss_deassert_reset ( lpss ) ;
if ( ! intel_lpss_has_idma ( lpss ) )
return ;
intel_lpss_set_remap_addr ( lpss ) ;
/* Make sure that SPI multiblock DMA transfers are re-enabled */
if ( lpss - > type = = LPSS_DEV_SPI )
writel ( value , lpss - > priv + LPSS_PRIV_SSP_REG ) ;
}
static void intel_lpss_unregister_clock_tree ( struct clk * clk )
{
struct clk * parent ;
while ( clk ) {
parent = clk_get_parent ( clk ) ;
clk_unregister ( clk ) ;
clk = parent ;
}
}
static int intel_lpss_register_clock_divider ( struct intel_lpss * lpss ,
const char * devname ,
struct clk * * clk )
{
char name [ 32 ] ;
struct clk * tmp = * clk ;
snprintf ( name , sizeof ( name ) , " %s-enable " , devname ) ;
tmp = clk_register_gate ( NULL , name , __clk_get_name ( tmp ) , 0 ,
lpss - > priv , 0 , 0 , NULL ) ;
if ( IS_ERR ( tmp ) )
return PTR_ERR ( tmp ) ;
snprintf ( name , sizeof ( name ) , " %s-div " , devname ) ;
tmp = clk_register_fractional_divider ( NULL , name , __clk_get_name ( tmp ) ,
0 , lpss - > priv , 1 , 15 , 16 , 15 , 0 ,
NULL ) ;
if ( IS_ERR ( tmp ) )
return PTR_ERR ( tmp ) ;
* clk = tmp ;
snprintf ( name , sizeof ( name ) , " %s-update " , devname ) ;
tmp = clk_register_gate ( NULL , name , __clk_get_name ( tmp ) ,
CLK_SET_RATE_PARENT , lpss - > priv , 31 , 0 , NULL ) ;
if ( IS_ERR ( tmp ) )
return PTR_ERR ( tmp ) ;
* clk = tmp ;
return 0 ;
}
static int intel_lpss_register_clock ( struct intel_lpss * lpss )
{
const struct mfd_cell * cell = lpss - > cell ;
struct clk * clk ;
char devname [ 24 ] ;
int ret ;
if ( ! lpss - > info - > clk_rate )
return 0 ;
/* Root clock */
2016-04-20 04:20:49 +03:00
clk = clk_register_fixed_rate ( NULL , dev_name ( lpss - > dev ) , NULL , 0 ,
lpss - > info - > clk_rate ) ;
2015-07-27 18:04:03 +03:00
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
snprintf ( devname , sizeof ( devname ) , " %s.%d " , cell - > name , lpss - > devid ) ;
/*
* Support for clock divider only if it has some preset value .
* Otherwise we assume that the divider is not used .
*/
if ( lpss - > type ! = LPSS_DEV_I2C ) {
ret = intel_lpss_register_clock_divider ( lpss , devname , & clk ) ;
if ( ret )
goto err_clk_register ;
}
ret = - ENOMEM ;
/* Clock for the host controller */
lpss - > clock = clkdev_create ( clk , lpss - > info - > clk_con_id , " %s " , devname ) ;
if ( ! lpss - > clock )
goto err_clk_register ;
lpss - > clk = clk ;
return 0 ;
err_clk_register :
intel_lpss_unregister_clock_tree ( clk ) ;
return ret ;
}
static void intel_lpss_unregister_clock ( struct intel_lpss * lpss )
{
if ( IS_ERR_OR_NULL ( lpss - > clk ) )
return ;
clkdev_drop ( lpss - > clock ) ;
intel_lpss_unregister_clock_tree ( lpss - > clk ) ;
}
int intel_lpss_probe ( struct device * dev ,
const struct intel_lpss_platform_info * info )
{
struct intel_lpss * lpss ;
int ret ;
if ( ! info | | ! info - > mem | | info - > irq < = 0 )
return - EINVAL ;
lpss = devm_kzalloc ( dev , sizeof ( * lpss ) , GFP_KERNEL ) ;
if ( ! lpss )
return - ENOMEM ;
lpss - > priv = devm_ioremap ( dev , info - > mem - > start + LPSS_PRIV_OFFSET ,
LPSS_PRIV_SIZE ) ;
if ( ! lpss - > priv )
return - ENOMEM ;
lpss - > info = info ;
lpss - > dev = dev ;
lpss - > caps = readl ( lpss - > priv + LPSS_PRIV_CAPS ) ;
dev_set_drvdata ( dev , lpss ) ;
ret = intel_lpss_assign_devs ( lpss ) ;
if ( ret )
return ret ;
2016-03-29 14:52:23 +03:00
lpss - > cell - > properties = info - > properties ;
2015-11-30 18:11:41 +03:00
2015-07-27 18:04:03 +03:00
intel_lpss_init_dev ( lpss ) ;
lpss - > devid = ida_simple_get ( & intel_lpss_devid_ida , 0 , 0 , GFP_KERNEL ) ;
if ( lpss - > devid < 0 )
return lpss - > devid ;
ret = intel_lpss_register_clock ( lpss ) ;
if ( ret )
goto err_clk_register ;
intel_lpss_ltr_expose ( lpss ) ;
ret = intel_lpss_debugfs_add ( lpss ) ;
if ( ret )
dev_warn ( dev , " Failed to create debugfs entries \n " ) ;
if ( intel_lpss_has_idma ( lpss ) ) {
/*
* Ensure the DMA driver is loaded before the host
* controller device appears , so that the host controller
* driver can request its DMA channels as early as
* possible .
*
* If the DMA module is not there that ' s OK as well .
*/
intel_lpss_request_dma_module ( LPSS_IDMA64_DRIVER_NAME ) ;
ret = mfd_add_devices ( dev , lpss - > devid , & intel_lpss_idma64_cell ,
1 , info - > mem , info - > irq , NULL ) ;
if ( ret )
dev_warn ( dev , " Failed to add %s, fallback to PIO \n " ,
LPSS_IDMA64_DRIVER_NAME ) ;
}
ret = mfd_add_devices ( dev , lpss - > devid , lpss - > cell ,
1 , info - > mem , info - > irq , NULL ) ;
if ( ret )
goto err_remove_ltr ;
return 0 ;
err_remove_ltr :
intel_lpss_debugfs_remove ( lpss ) ;
intel_lpss_ltr_hide ( lpss ) ;
2016-01-22 17:48:46 +03:00
intel_lpss_unregister_clock ( lpss ) ;
2015-07-27 18:04:03 +03:00
err_clk_register :
ida_simple_remove ( & intel_lpss_devid_ida , lpss - > devid ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( intel_lpss_probe ) ;
void intel_lpss_remove ( struct device * dev )
{
struct intel_lpss * lpss = dev_get_drvdata ( dev ) ;
mfd_remove_devices ( dev ) ;
intel_lpss_debugfs_remove ( lpss ) ;
intel_lpss_ltr_hide ( lpss ) ;
intel_lpss_unregister_clock ( lpss ) ;
ida_simple_remove ( & intel_lpss_devid_ida , lpss - > devid ) ;
}
EXPORT_SYMBOL_GPL ( intel_lpss_remove ) ;
static int resume_lpss_device ( struct device * dev , void * data )
{
pm_runtime_resume ( dev ) ;
return 0 ;
}
int intel_lpss_prepare ( struct device * dev )
{
/*
* Resume both child devices before entering system sleep . This
* ensures that they are in proper state before they get suspended .
*/
device_for_each_child_reverse ( dev , NULL , resume_lpss_device ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( intel_lpss_prepare ) ;
int intel_lpss_suspend ( struct device * dev )
{
2016-04-18 15:14:56 +03:00
struct intel_lpss * lpss = dev_get_drvdata ( dev ) ;
unsigned int i ;
/* Save device context */
for ( i = 0 ; i < LPSS_PRIV_REG_COUNT ; i + + )
lpss - > priv_ctx [ i ] = readl ( lpss - > priv + i * 4 ) ;
/* Put the device into reset state */
writel ( 0 , lpss - > priv + LPSS_PRIV_RESETS ) ;
2015-07-27 18:04:03 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( intel_lpss_suspend ) ;
int intel_lpss_resume ( struct device * dev )
{
struct intel_lpss * lpss = dev_get_drvdata ( dev ) ;
2016-04-18 15:14:56 +03:00
unsigned int i ;
2015-07-27 18:04:03 +03:00
2016-04-18 15:14:56 +03:00
intel_lpss_deassert_reset ( lpss ) ;
/* Restore device context */
for ( i = 0 ; i < LPSS_PRIV_REG_COUNT ; i + + )
writel ( lpss - > priv_ctx [ i ] , lpss - > priv + i * 4 ) ;
2015-07-27 18:04:03 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( intel_lpss_resume ) ;
static int __init intel_lpss_init ( void )
{
intel_lpss_debugfs = debugfs_create_dir ( " intel_lpss " , NULL ) ;
return 0 ;
}
module_init ( intel_lpss_init ) ;
static void __exit intel_lpss_exit ( void )
{
debugfs_remove ( intel_lpss_debugfs ) ;
}
module_exit ( intel_lpss_exit ) ;
MODULE_AUTHOR ( " Andy Shevchenko <andriy.shevchenko@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Mika Westerberg <mika.westerberg@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Heikki Krogerus <heikki.krogerus@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Jarkko Nikula <jarkko.nikula@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel LPSS core driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;