blob: ad7cd9cfaee04908010ce5be6d6778deceb25f6e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Versatile OF physmap driver add-on
*
* Copyright (c) 2016, Linaro Limited
* Author: Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/export.h>
#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>
#include "physmap-versatile.h"
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;
void __iomem *ebi_base;
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;
}