2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-01-26 11:58:07 +01:00
/*
* Versatile OF physmap driver add - on
*
* Copyright ( c ) 2016 , Linaro Limited
* Author : Linus Walleij < linus . walleij @ linaro . org >
*/
2016-04-26 01:01:40 +02:00
# include <linux/export.h>
2016-01-26 11:58:07 +01:00
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/mtd/map.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include <linux/bitops.h>
2018-10-19 09:49:05 +02:00
# include "physmap-versatile.h"
2016-01-26 11:58:07 +01:00
static struct regmap * syscon_regmap ;
enum versatile_flashprot {
INTEGRATOR_AP_FLASHPROT ,
INTEGRATOR_CP_FLASHPROT ,
VERSATILE_FLASHPROT ,
REALVIEW_FLASHPROT ,
} ;
static const struct of_device_id syscon_match [ ] = {
{
. compatible = " arm,integrator-ap-syscon " ,
. data = ( void * ) INTEGRATOR_AP_FLASHPROT ,
} ,
{
. compatible = " arm,integrator-cp-syscon " ,
. data = ( void * ) INTEGRATOR_CP_FLASHPROT ,
} ,
{
. compatible = " arm,core-module-versatile " ,
. data = ( void * ) VERSATILE_FLASHPROT ,
} ,
{
. compatible = " arm,realview-eb-syscon " ,
. data = ( void * ) REALVIEW_FLASHPROT ,
} ,
{
. compatible = " arm,realview-pb1176-syscon " ,
. data = ( void * ) REALVIEW_FLASHPROT ,
} ,
{
. compatible = " arm,realview-pb11mp-syscon " ,
. data = ( void * ) REALVIEW_FLASHPROT ,
} ,
{
. compatible = " arm,realview-pba8-syscon " ,
. data = ( void * ) REALVIEW_FLASHPROT ,
} ,
{
. compatible = " arm,realview-pbx-syscon " ,
. data = ( void * ) REALVIEW_FLASHPROT ,
} ,
{ } ,
} ;
/*
* Flash protection handling for the Integrator / AP
*/
# define INTEGRATOR_SC_CTRLS_OFFSET 0x08
# define INTEGRATOR_SC_CTRLC_OFFSET 0x0C
# define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1)
# define INTEGRATOR_SC_CTRL_FLWP BIT(2)
# define INTEGRATOR_EBI_CSR1_OFFSET 0x04
/* The manual says bit 2, the code says bit 3, trust the code */
# define INTEGRATOR_EBI_WRITE_ENABLE BIT(3)
# define INTEGRATOR_EBI_LOCK_OFFSET 0x20
# define INTEGRATOR_EBI_LOCK_VAL 0xA05F
static const struct of_device_id ebi_match [ ] = {
{ . compatible = " arm,external-bus-interface " } ,
{ } ,
} ;
static int ap_flash_init ( struct platform_device * pdev )
{
struct device_node * ebi ;
2017-07-15 22:07:38 +02:00
void __iomem * ebi_base ;
2016-01-26 11:58:07 +01:00
u32 val ;
int ret ;
/* Look up the EBI */
ebi = of_find_matching_node ( NULL , ebi_match ) ;
if ( ! ebi ) {
return - ENODEV ;
}
ebi_base = of_iomap ( ebi , 0 ) ;
if ( ! ebi_base )
return - ENODEV ;
/* Clear VPP and write protection bits */
ret = regmap_write ( syscon_regmap ,
INTEGRATOR_SC_CTRLC_OFFSET ,
INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP ) ;
if ( ret )
dev_err ( & pdev - > dev , " error clearing Integrator VPP/WP \n " ) ;
/* Unlock the EBI */
writel ( INTEGRATOR_EBI_LOCK_VAL , ebi_base + INTEGRATOR_EBI_LOCK_OFFSET ) ;
/* Enable write cycles on the EBI, CSR1 (flash) */
val = readl ( ebi_base + INTEGRATOR_EBI_CSR1_OFFSET ) ;
val | = INTEGRATOR_EBI_WRITE_ENABLE ;
writel ( val , ebi_base + INTEGRATOR_EBI_CSR1_OFFSET ) ;
/* Lock the EBI again */
writel ( 0 , ebi_base + INTEGRATOR_EBI_LOCK_OFFSET ) ;
iounmap ( ebi_base ) ;
return 0 ;
}
static void ap_flash_set_vpp ( struct map_info * map , int on )
{
int ret ;
if ( on ) {
ret = regmap_write ( syscon_regmap ,
INTEGRATOR_SC_CTRLS_OFFSET ,
INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP ) ;
if ( ret )
pr_err ( " error enabling AP VPP \n " ) ;
} else {
ret = regmap_write ( syscon_regmap ,
INTEGRATOR_SC_CTRLC_OFFSET ,
INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP ) ;
if ( ret )
pr_err ( " error disabling AP VPP \n " ) ;
}
}
/*
* Flash protection handling for the Integrator / CP
*/
# define INTCP_FLASHPROG_OFFSET 0x04
# define CINTEGRATOR_FLVPPEN BIT(0)
# define CINTEGRATOR_FLWREN BIT(1)
# define CINTEGRATOR_FLMASK BIT(0)|BIT(1)
static void cp_flash_set_vpp ( struct map_info * map , int on )
{
int ret ;
if ( on ) {
ret = regmap_update_bits ( syscon_regmap ,
INTCP_FLASHPROG_OFFSET ,
CINTEGRATOR_FLMASK ,
CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN ) ;
if ( ret )
pr_err ( " error setting CP VPP \n " ) ;
} else {
ret = regmap_update_bits ( syscon_regmap ,
INTCP_FLASHPROG_OFFSET ,
CINTEGRATOR_FLMASK ,
0 ) ;
if ( ret )
pr_err ( " error setting CP VPP \n " ) ;
}
}
/*
* Flash protection handling for the Versatiles and RealViews
*/
# define VERSATILE_SYS_FLASH_OFFSET 0x4C
static void versatile_flash_set_vpp ( struct map_info * map , int on )
{
int ret ;
ret = regmap_update_bits ( syscon_regmap , VERSATILE_SYS_FLASH_OFFSET ,
0x01 , ! ! on ) ;
if ( ret )
pr_err ( " error setting Versatile VPP \n " ) ;
}
int of_flash_probe_versatile ( struct platform_device * pdev ,
struct device_node * np ,
struct map_info * map )
{
struct device_node * sysnp ;
const struct of_device_id * devid ;
struct regmap * rmap ;
static enum versatile_flashprot versatile_flashprot ;
int ret ;
/* Not all flash chips use this protection line */
if ( ! of_device_is_compatible ( np , " arm,versatile-flash " ) )
return 0 ;
/* For first chip probed, look up the syscon regmap */
if ( ! syscon_regmap ) {
sysnp = of_find_matching_node_and_match ( NULL ,
syscon_match ,
& devid ) ;
if ( ! sysnp )
return - ENODEV ;
versatile_flashprot = ( enum versatile_flashprot ) devid - > data ;
rmap = syscon_node_to_regmap ( sysnp ) ;
if ( IS_ERR ( rmap ) )
return PTR_ERR ( rmap ) ;
syscon_regmap = rmap ;
}
switch ( versatile_flashprot ) {
case INTEGRATOR_AP_FLASHPROT :
ret = ap_flash_init ( pdev ) ;
if ( ret )
return ret ;
map - > set_vpp = ap_flash_set_vpp ;
dev_info ( & pdev - > dev , " Integrator/AP flash protection \n " ) ;
break ;
case INTEGRATOR_CP_FLASHPROT :
map - > set_vpp = cp_flash_set_vpp ;
dev_info ( & pdev - > dev , " Integrator/CP flash protection \n " ) ;
break ;
case VERSATILE_FLASHPROT :
case REALVIEW_FLASHPROT :
map - > set_vpp = versatile_flash_set_vpp ;
dev_info ( & pdev - > dev , " versatile/realview flash protection \n " ) ;
break ;
default :
dev_info ( & pdev - > dev , " device marked as Versatile flash "
" but no system controller was found \n " ) ;
break ;
}
return 0 ;
}