1 /* 2 * Versatile OF physmap driver add-on 3 * 4 * Copyright (c) 2016, Linaro Limited 5 * Author: Linus Walleij <linus.walleij@linaro.org> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 20 * MA 02111-1307 USA 21 */ 22 #include <linux/export.h> 23 #include <linux/io.h> 24 #include <linux/of.h> 25 #include <linux/of_address.h> 26 #include <linux/of_device.h> 27 #include <linux/mtd/map.h> 28 #include <linux/mfd/syscon.h> 29 #include <linux/regmap.h> 30 #include <linux/bitops.h> 31 #include "physmap-versatile.h" 32 33 static struct regmap *syscon_regmap; 34 35 enum versatile_flashprot { 36 INTEGRATOR_AP_FLASHPROT, 37 INTEGRATOR_CP_FLASHPROT, 38 VERSATILE_FLASHPROT, 39 REALVIEW_FLASHPROT, 40 }; 41 42 static const struct of_device_id syscon_match[] = { 43 { 44 .compatible = "arm,integrator-ap-syscon", 45 .data = (void *)INTEGRATOR_AP_FLASHPROT, 46 }, 47 { 48 .compatible = "arm,integrator-cp-syscon", 49 .data = (void *)INTEGRATOR_CP_FLASHPROT, 50 }, 51 { 52 .compatible = "arm,core-module-versatile", 53 .data = (void *)VERSATILE_FLASHPROT, 54 }, 55 { 56 .compatible = "arm,realview-eb-syscon", 57 .data = (void *)REALVIEW_FLASHPROT, 58 }, 59 { 60 .compatible = "arm,realview-pb1176-syscon", 61 .data = (void *)REALVIEW_FLASHPROT, 62 }, 63 { 64 .compatible = "arm,realview-pb11mp-syscon", 65 .data = (void *)REALVIEW_FLASHPROT, 66 }, 67 { 68 .compatible = "arm,realview-pba8-syscon", 69 .data = (void *)REALVIEW_FLASHPROT, 70 }, 71 { 72 .compatible = "arm,realview-pbx-syscon", 73 .data = (void *)REALVIEW_FLASHPROT, 74 }, 75 {}, 76 }; 77 78 /* 79 * Flash protection handling for the Integrator/AP 80 */ 81 #define INTEGRATOR_SC_CTRLS_OFFSET 0x08 82 #define INTEGRATOR_SC_CTRLC_OFFSET 0x0C 83 #define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) 84 #define INTEGRATOR_SC_CTRL_FLWP BIT(2) 85 86 #define INTEGRATOR_EBI_CSR1_OFFSET 0x04 87 /* The manual says bit 2, the code says bit 3, trust the code */ 88 #define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) 89 #define INTEGRATOR_EBI_LOCK_OFFSET 0x20 90 #define INTEGRATOR_EBI_LOCK_VAL 0xA05F 91 92 static const struct of_device_id ebi_match[] = { 93 { .compatible = "arm,external-bus-interface"}, 94 { }, 95 }; 96 97 static int ap_flash_init(struct platform_device *pdev) 98 { 99 struct device_node *ebi; 100 void __iomem *ebi_base; 101 u32 val; 102 int ret; 103 104 /* Look up the EBI */ 105 ebi = of_find_matching_node(NULL, ebi_match); 106 if (!ebi) { 107 return -ENODEV; 108 } 109 ebi_base = of_iomap(ebi, 0); 110 if (!ebi_base) 111 return -ENODEV; 112 113 /* Clear VPP and write protection bits */ 114 ret = regmap_write(syscon_regmap, 115 INTEGRATOR_SC_CTRLC_OFFSET, 116 INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 117 if (ret) 118 dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); 119 120 /* Unlock the EBI */ 121 writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 122 123 /* Enable write cycles on the EBI, CSR1 (flash) */ 124 val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 125 val |= INTEGRATOR_EBI_WRITE_ENABLE; 126 writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); 127 128 /* Lock the EBI again */ 129 writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); 130 iounmap(ebi_base); 131 132 return 0; 133 } 134 135 static void ap_flash_set_vpp(struct map_info *map, int on) 136 { 137 int ret; 138 139 if (on) { 140 ret = regmap_write(syscon_regmap, 141 INTEGRATOR_SC_CTRLS_OFFSET, 142 INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 143 if (ret) 144 pr_err("error enabling AP VPP\n"); 145 } else { 146 ret = regmap_write(syscon_regmap, 147 INTEGRATOR_SC_CTRLC_OFFSET, 148 INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); 149 if (ret) 150 pr_err("error disabling AP VPP\n"); 151 } 152 } 153 154 /* 155 * Flash protection handling for the Integrator/CP 156 */ 157 158 #define INTCP_FLASHPROG_OFFSET 0x04 159 #define CINTEGRATOR_FLVPPEN BIT(0) 160 #define CINTEGRATOR_FLWREN BIT(1) 161 #define CINTEGRATOR_FLMASK BIT(0)|BIT(1) 162 163 static void cp_flash_set_vpp(struct map_info *map, int on) 164 { 165 int ret; 166 167 if (on) { 168 ret = regmap_update_bits(syscon_regmap, 169 INTCP_FLASHPROG_OFFSET, 170 CINTEGRATOR_FLMASK, 171 CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); 172 if (ret) 173 pr_err("error setting CP VPP\n"); 174 } else { 175 ret = regmap_update_bits(syscon_regmap, 176 INTCP_FLASHPROG_OFFSET, 177 CINTEGRATOR_FLMASK, 178 0); 179 if (ret) 180 pr_err("error setting CP VPP\n"); 181 } 182 } 183 184 /* 185 * Flash protection handling for the Versatiles and RealViews 186 */ 187 188 #define VERSATILE_SYS_FLASH_OFFSET 0x4C 189 190 static void versatile_flash_set_vpp(struct map_info *map, int on) 191 { 192 int ret; 193 194 ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, 195 0x01, !!on); 196 if (ret) 197 pr_err("error setting Versatile VPP\n"); 198 } 199 200 int of_flash_probe_versatile(struct platform_device *pdev, 201 struct device_node *np, 202 struct map_info *map) 203 { 204 struct device_node *sysnp; 205 const struct of_device_id *devid; 206 struct regmap *rmap; 207 static enum versatile_flashprot versatile_flashprot; 208 int ret; 209 210 /* Not all flash chips use this protection line */ 211 if (!of_device_is_compatible(np, "arm,versatile-flash")) 212 return 0; 213 214 /* For first chip probed, look up the syscon regmap */ 215 if (!syscon_regmap) { 216 sysnp = of_find_matching_node_and_match(NULL, 217 syscon_match, 218 &devid); 219 if (!sysnp) 220 return -ENODEV; 221 222 versatile_flashprot = (enum versatile_flashprot)devid->data; 223 rmap = syscon_node_to_regmap(sysnp); 224 if (IS_ERR(rmap)) 225 return PTR_ERR(rmap); 226 227 syscon_regmap = rmap; 228 } 229 230 switch (versatile_flashprot) { 231 case INTEGRATOR_AP_FLASHPROT: 232 ret = ap_flash_init(pdev); 233 if (ret) 234 return ret; 235 map->set_vpp = ap_flash_set_vpp; 236 dev_info(&pdev->dev, "Integrator/AP flash protection\n"); 237 break; 238 case INTEGRATOR_CP_FLASHPROT: 239 map->set_vpp = cp_flash_set_vpp; 240 dev_info(&pdev->dev, "Integrator/CP flash protection\n"); 241 break; 242 case VERSATILE_FLASHPROT: 243 case REALVIEW_FLASHPROT: 244 map->set_vpp = versatile_flash_set_vpp; 245 dev_info(&pdev->dev, "versatile/realview flash protection\n"); 246 break; 247 default: 248 dev_info(&pdev->dev, "device marked as Versatile flash " 249 "but no system controller was found\n"); 250 break; 251 } 252 253 return 0; 254 } 255