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