// 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; }