// 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/mtd/map.h>#include <linux/mfd/syscon.h>#include <linux/platform_device.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); of_node_put(ebi); 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 = (uintptr_t)devid->data; rmap = syscon_node_to_regmap(sysnp); of_node_put(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;}
// 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/mtd/map.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.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);
of_node_put(ebi);
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 = (uintptr_t)devid->data;
rmap = syscon_node_to_regmap(sysnp);
of_node_put(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;
}