162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Versatile OF physmap driver add-on 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2016, Linaro Limited 662306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/mtd/map.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/bitops.h> 1762306a36Sopenharmony_ci#include "physmap-versatile.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct regmap *syscon_regmap; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cienum versatile_flashprot { 2262306a36Sopenharmony_ci INTEGRATOR_AP_FLASHPROT, 2362306a36Sopenharmony_ci INTEGRATOR_CP_FLASHPROT, 2462306a36Sopenharmony_ci VERSATILE_FLASHPROT, 2562306a36Sopenharmony_ci REALVIEW_FLASHPROT, 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct of_device_id syscon_match[] = { 2962306a36Sopenharmony_ci { 3062306a36Sopenharmony_ci .compatible = "arm,integrator-ap-syscon", 3162306a36Sopenharmony_ci .data = (void *)INTEGRATOR_AP_FLASHPROT, 3262306a36Sopenharmony_ci }, 3362306a36Sopenharmony_ci { 3462306a36Sopenharmony_ci .compatible = "arm,integrator-cp-syscon", 3562306a36Sopenharmony_ci .data = (void *)INTEGRATOR_CP_FLASHPROT, 3662306a36Sopenharmony_ci }, 3762306a36Sopenharmony_ci { 3862306a36Sopenharmony_ci .compatible = "arm,core-module-versatile", 3962306a36Sopenharmony_ci .data = (void *)VERSATILE_FLASHPROT, 4062306a36Sopenharmony_ci }, 4162306a36Sopenharmony_ci { 4262306a36Sopenharmony_ci .compatible = "arm,realview-eb-syscon", 4362306a36Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 4462306a36Sopenharmony_ci }, 4562306a36Sopenharmony_ci { 4662306a36Sopenharmony_ci .compatible = "arm,realview-pb1176-syscon", 4762306a36Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci { 5062306a36Sopenharmony_ci .compatible = "arm,realview-pb11mp-syscon", 5162306a36Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 5262306a36Sopenharmony_ci }, 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci .compatible = "arm,realview-pba8-syscon", 5562306a36Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci { 5862306a36Sopenharmony_ci .compatible = "arm,realview-pbx-syscon", 5962306a36Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci {}, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * Flash protection handling for the Integrator/AP 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define INTEGRATOR_SC_CTRLS_OFFSET 0x08 6862306a36Sopenharmony_ci#define INTEGRATOR_SC_CTRLC_OFFSET 0x0C 6962306a36Sopenharmony_ci#define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) 7062306a36Sopenharmony_ci#define INTEGRATOR_SC_CTRL_FLWP BIT(2) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define INTEGRATOR_EBI_CSR1_OFFSET 0x04 7362306a36Sopenharmony_ci/* The manual says bit 2, the code says bit 3, trust the code */ 7462306a36Sopenharmony_ci#define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) 7562306a36Sopenharmony_ci#define INTEGRATOR_EBI_LOCK_OFFSET 0x20 7662306a36Sopenharmony_ci#define INTEGRATOR_EBI_LOCK_VAL 0xA05F 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct of_device_id ebi_match[] = { 7962306a36Sopenharmony_ci { .compatible = "arm,external-bus-interface"}, 8062306a36Sopenharmony_ci { }, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int ap_flash_init(struct platform_device *pdev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct device_node *ebi; 8662306a36Sopenharmony_ci void __iomem *ebi_base; 8762306a36Sopenharmony_ci u32 val; 8862306a36Sopenharmony_ci int ret; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Look up the EBI */ 9162306a36Sopenharmony_ci ebi = of_find_matching_node(NULL, ebi_match); 9262306a36Sopenharmony_ci if (!ebi) { 9362306a36Sopenharmony_ci return -ENODEV; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci ebi_base = of_iomap(ebi, 0); 9662306a36Sopenharmony_ci of_node_put(ebi); 9762306a36Sopenharmony_ci if (!ebi_base) 9862306a36Sopenharmony_ci return -ENODEV; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Clear VPP and write protection bits */ 10162306a36Sopenharmony_ci ret = regmap_write(syscon_regmap, 10262306a36Sopenharmony_ci INTEGRATOR_SC_CTRLC_OFFSET, 10362306a36Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 10462306a36Sopenharmony_ci if (ret) 10562306a36Sopenharmony_ci dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Unlock the EBI */ 10862306a36Sopenharmony_ci writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Enable write cycles on the EBI, CSR1 (flash) */ 11162306a36Sopenharmony_ci val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 11262306a36Sopenharmony_ci val |= INTEGRATOR_EBI_WRITE_ENABLE; 11362306a36Sopenharmony_ci writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Lock the EBI again */ 11662306a36Sopenharmony_ci writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 11762306a36Sopenharmony_ci iounmap(ebi_base); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void ap_flash_set_vpp(struct map_info *map, int on) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (on) { 12762306a36Sopenharmony_ci ret = regmap_write(syscon_regmap, 12862306a36Sopenharmony_ci INTEGRATOR_SC_CTRLS_OFFSET, 12962306a36Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci pr_err("error enabling AP VPP\n"); 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci ret = regmap_write(syscon_regmap, 13462306a36Sopenharmony_ci INTEGRATOR_SC_CTRLC_OFFSET, 13562306a36Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci pr_err("error disabling AP VPP\n"); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Flash protection handling for the Integrator/CP 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define INTCP_FLASHPROG_OFFSET 0x04 14662306a36Sopenharmony_ci#define CINTEGRATOR_FLVPPEN BIT(0) 14762306a36Sopenharmony_ci#define CINTEGRATOR_FLWREN BIT(1) 14862306a36Sopenharmony_ci#define CINTEGRATOR_FLMASK BIT(0)|BIT(1) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void cp_flash_set_vpp(struct map_info *map, int on) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (on) { 15562306a36Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, 15662306a36Sopenharmony_ci INTCP_FLASHPROG_OFFSET, 15762306a36Sopenharmony_ci CINTEGRATOR_FLMASK, 15862306a36Sopenharmony_ci CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); 15962306a36Sopenharmony_ci if (ret) 16062306a36Sopenharmony_ci pr_err("error setting CP VPP\n"); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, 16362306a36Sopenharmony_ci INTCP_FLASHPROG_OFFSET, 16462306a36Sopenharmony_ci CINTEGRATOR_FLMASK, 16562306a36Sopenharmony_ci 0); 16662306a36Sopenharmony_ci if (ret) 16762306a36Sopenharmony_ci pr_err("error setting CP VPP\n"); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * Flash protection handling for the Versatiles and RealViews 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define VERSATILE_SYS_FLASH_OFFSET 0x4C 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void versatile_flash_set_vpp(struct map_info *map, int on) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, 18262306a36Sopenharmony_ci 0x01, !!on); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci pr_err("error setting Versatile VPP\n"); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciint of_flash_probe_versatile(struct platform_device *pdev, 18862306a36Sopenharmony_ci struct device_node *np, 18962306a36Sopenharmony_ci struct map_info *map) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct device_node *sysnp; 19262306a36Sopenharmony_ci const struct of_device_id *devid; 19362306a36Sopenharmony_ci struct regmap *rmap; 19462306a36Sopenharmony_ci static enum versatile_flashprot versatile_flashprot; 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Not all flash chips use this protection line */ 19862306a36Sopenharmony_ci if (!of_device_is_compatible(np, "arm,versatile-flash")) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* For first chip probed, look up the syscon regmap */ 20262306a36Sopenharmony_ci if (!syscon_regmap) { 20362306a36Sopenharmony_ci sysnp = of_find_matching_node_and_match(NULL, 20462306a36Sopenharmony_ci syscon_match, 20562306a36Sopenharmony_ci &devid); 20662306a36Sopenharmony_ci if (!sysnp) 20762306a36Sopenharmony_ci return -ENODEV; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci versatile_flashprot = (uintptr_t)devid->data; 21062306a36Sopenharmony_ci rmap = syscon_node_to_regmap(sysnp); 21162306a36Sopenharmony_ci of_node_put(sysnp); 21262306a36Sopenharmony_ci if (IS_ERR(rmap)) 21362306a36Sopenharmony_ci return PTR_ERR(rmap); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci syscon_regmap = rmap; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (versatile_flashprot) { 21962306a36Sopenharmony_ci case INTEGRATOR_AP_FLASHPROT: 22062306a36Sopenharmony_ci ret = ap_flash_init(pdev); 22162306a36Sopenharmony_ci if (ret) 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci map->set_vpp = ap_flash_set_vpp; 22462306a36Sopenharmony_ci dev_info(&pdev->dev, "Integrator/AP flash protection\n"); 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case INTEGRATOR_CP_FLASHPROT: 22762306a36Sopenharmony_ci map->set_vpp = cp_flash_set_vpp; 22862306a36Sopenharmony_ci dev_info(&pdev->dev, "Integrator/CP flash protection\n"); 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case VERSATILE_FLASHPROT: 23162306a36Sopenharmony_ci case REALVIEW_FLASHPROT: 23262306a36Sopenharmony_ci map->set_vpp = versatile_flash_set_vpp; 23362306a36Sopenharmony_ci dev_info(&pdev->dev, "versatile/realview flash protection\n"); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci dev_info(&pdev->dev, "device marked as Versatile flash " 23762306a36Sopenharmony_ci "but no system controller was found\n"); 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 243