18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Versatile OF physmap driver add-on 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016, Linaro Limited 68c2ecf20Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/bitops.h> 178c2ecf20Sopenharmony_ci#include "physmap-versatile.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct regmap *syscon_regmap; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cienum versatile_flashprot { 228c2ecf20Sopenharmony_ci INTEGRATOR_AP_FLASHPROT, 238c2ecf20Sopenharmony_ci INTEGRATOR_CP_FLASHPROT, 248c2ecf20Sopenharmony_ci VERSATILE_FLASHPROT, 258c2ecf20Sopenharmony_ci REALVIEW_FLASHPROT, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct of_device_id syscon_match[] = { 298c2ecf20Sopenharmony_ci { 308c2ecf20Sopenharmony_ci .compatible = "arm,integrator-ap-syscon", 318c2ecf20Sopenharmony_ci .data = (void *)INTEGRATOR_AP_FLASHPROT, 328c2ecf20Sopenharmony_ci }, 338c2ecf20Sopenharmony_ci { 348c2ecf20Sopenharmony_ci .compatible = "arm,integrator-cp-syscon", 358c2ecf20Sopenharmony_ci .data = (void *)INTEGRATOR_CP_FLASHPROT, 368c2ecf20Sopenharmony_ci }, 378c2ecf20Sopenharmony_ci { 388c2ecf20Sopenharmony_ci .compatible = "arm,core-module-versatile", 398c2ecf20Sopenharmony_ci .data = (void *)VERSATILE_FLASHPROT, 408c2ecf20Sopenharmony_ci }, 418c2ecf20Sopenharmony_ci { 428c2ecf20Sopenharmony_ci .compatible = "arm,realview-eb-syscon", 438c2ecf20Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 448c2ecf20Sopenharmony_ci }, 458c2ecf20Sopenharmony_ci { 468c2ecf20Sopenharmony_ci .compatible = "arm,realview-pb1176-syscon", 478c2ecf20Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 488c2ecf20Sopenharmony_ci }, 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .compatible = "arm,realview-pb11mp-syscon", 518c2ecf20Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 528c2ecf20Sopenharmony_ci }, 538c2ecf20Sopenharmony_ci { 548c2ecf20Sopenharmony_ci .compatible = "arm,realview-pba8-syscon", 558c2ecf20Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 568c2ecf20Sopenharmony_ci }, 578c2ecf20Sopenharmony_ci { 588c2ecf20Sopenharmony_ci .compatible = "arm,realview-pbx-syscon", 598c2ecf20Sopenharmony_ci .data = (void *)REALVIEW_FLASHPROT, 608c2ecf20Sopenharmony_ci }, 618c2ecf20Sopenharmony_ci {}, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Flash protection handling for the Integrator/AP 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci#define INTEGRATOR_SC_CTRLS_OFFSET 0x08 688c2ecf20Sopenharmony_ci#define INTEGRATOR_SC_CTRLC_OFFSET 0x0C 698c2ecf20Sopenharmony_ci#define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) 708c2ecf20Sopenharmony_ci#define INTEGRATOR_SC_CTRL_FLWP BIT(2) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define INTEGRATOR_EBI_CSR1_OFFSET 0x04 738c2ecf20Sopenharmony_ci/* The manual says bit 2, the code says bit 3, trust the code */ 748c2ecf20Sopenharmony_ci#define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) 758c2ecf20Sopenharmony_ci#define INTEGRATOR_EBI_LOCK_OFFSET 0x20 768c2ecf20Sopenharmony_ci#define INTEGRATOR_EBI_LOCK_VAL 0xA05F 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const struct of_device_id ebi_match[] = { 798c2ecf20Sopenharmony_ci { .compatible = "arm,external-bus-interface"}, 808c2ecf20Sopenharmony_ci { }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int ap_flash_init(struct platform_device *pdev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct device_node *ebi; 868c2ecf20Sopenharmony_ci void __iomem *ebi_base; 878c2ecf20Sopenharmony_ci u32 val; 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Look up the EBI */ 918c2ecf20Sopenharmony_ci ebi = of_find_matching_node(NULL, ebi_match); 928c2ecf20Sopenharmony_ci if (!ebi) { 938c2ecf20Sopenharmony_ci return -ENODEV; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci ebi_base = of_iomap(ebi, 0); 968c2ecf20Sopenharmony_ci of_node_put(ebi); 978c2ecf20Sopenharmony_ci if (!ebi_base) 988c2ecf20Sopenharmony_ci return -ENODEV; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Clear VPP and write protection bits */ 1018c2ecf20Sopenharmony_ci ret = regmap_write(syscon_regmap, 1028c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRLC_OFFSET, 1038c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 1048c2ecf20Sopenharmony_ci if (ret) 1058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Unlock the EBI */ 1088c2ecf20Sopenharmony_ci writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Enable write cycles on the EBI, CSR1 (flash) */ 1118c2ecf20Sopenharmony_ci val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 1128c2ecf20Sopenharmony_ci val |= INTEGRATOR_EBI_WRITE_ENABLE; 1138c2ecf20Sopenharmony_ci writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Lock the EBI again */ 1168c2ecf20Sopenharmony_ci writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 1178c2ecf20Sopenharmony_ci iounmap(ebi_base); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void ap_flash_set_vpp(struct map_info *map, int on) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (on) { 1278c2ecf20Sopenharmony_ci ret = regmap_write(syscon_regmap, 1288c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRLS_OFFSET, 1298c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci pr_err("error enabling AP VPP\n"); 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci ret = regmap_write(syscon_regmap, 1348c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRLC_OFFSET, 1358c2ecf20Sopenharmony_ci INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci pr_err("error disabling AP VPP\n"); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Flash protection handling for the Integrator/CP 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define INTCP_FLASHPROG_OFFSET 0x04 1468c2ecf20Sopenharmony_ci#define CINTEGRATOR_FLVPPEN BIT(0) 1478c2ecf20Sopenharmony_ci#define CINTEGRATOR_FLWREN BIT(1) 1488c2ecf20Sopenharmony_ci#define CINTEGRATOR_FLMASK BIT(0)|BIT(1) 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void cp_flash_set_vpp(struct map_info *map, int on) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (on) { 1558c2ecf20Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, 1568c2ecf20Sopenharmony_ci INTCP_FLASHPROG_OFFSET, 1578c2ecf20Sopenharmony_ci CINTEGRATOR_FLMASK, 1588c2ecf20Sopenharmony_ci CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci pr_err("error setting CP VPP\n"); 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, 1638c2ecf20Sopenharmony_ci INTCP_FLASHPROG_OFFSET, 1648c2ecf20Sopenharmony_ci CINTEGRATOR_FLMASK, 1658c2ecf20Sopenharmony_ci 0); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci pr_err("error setting CP VPP\n"); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * Flash protection handling for the Versatiles and RealViews 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define VERSATILE_SYS_FLASH_OFFSET 0x4C 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void versatile_flash_set_vpp(struct map_info *map, int on) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, 1828c2ecf20Sopenharmony_ci 0x01, !!on); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci pr_err("error setting Versatile VPP\n"); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciint of_flash_probe_versatile(struct platform_device *pdev, 1888c2ecf20Sopenharmony_ci struct device_node *np, 1898c2ecf20Sopenharmony_ci struct map_info *map) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct device_node *sysnp; 1928c2ecf20Sopenharmony_ci const struct of_device_id *devid; 1938c2ecf20Sopenharmony_ci struct regmap *rmap; 1948c2ecf20Sopenharmony_ci static enum versatile_flashprot versatile_flashprot; 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Not all flash chips use this protection line */ 1988c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "arm,versatile-flash")) 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* For first chip probed, look up the syscon regmap */ 2028c2ecf20Sopenharmony_ci if (!syscon_regmap) { 2038c2ecf20Sopenharmony_ci sysnp = of_find_matching_node_and_match(NULL, 2048c2ecf20Sopenharmony_ci syscon_match, 2058c2ecf20Sopenharmony_ci &devid); 2068c2ecf20Sopenharmony_ci if (!sysnp) 2078c2ecf20Sopenharmony_ci return -ENODEV; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci versatile_flashprot = (enum versatile_flashprot)devid->data; 2108c2ecf20Sopenharmony_ci rmap = syscon_node_to_regmap(sysnp); 2118c2ecf20Sopenharmony_ci of_node_put(sysnp); 2128c2ecf20Sopenharmony_ci if (IS_ERR(rmap)) 2138c2ecf20Sopenharmony_ci return PTR_ERR(rmap); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci syscon_regmap = rmap; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci switch (versatile_flashprot) { 2198c2ecf20Sopenharmony_ci case INTEGRATOR_AP_FLASHPROT: 2208c2ecf20Sopenharmony_ci ret = ap_flash_init(pdev); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci map->set_vpp = ap_flash_set_vpp; 2248c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Integrator/AP flash protection\n"); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case INTEGRATOR_CP_FLASHPROT: 2278c2ecf20Sopenharmony_ci map->set_vpp = cp_flash_set_vpp; 2288c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Integrator/CP flash protection\n"); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case VERSATILE_FLASHPROT: 2318c2ecf20Sopenharmony_ci case REALVIEW_FLASHPROT: 2328c2ecf20Sopenharmony_ci map->set_vpp = versatile_flash_set_vpp; 2338c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "versatile/realview flash protection\n"); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "device marked as Versatile flash " 2378c2ecf20Sopenharmony_ci "but no system controller was found\n"); 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 243