1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018-2019, Intel Corporation. 4 * Copyright (C) 2012 Freescale Semiconductor, Inc. 5 * Copyright (C) 2012 Linaro Ltd. 6 * 7 * Based on syscon driver. 8 */ 9 10 #include <linux/arm-smccc.h> 11 #include <linux/err.h> 12 #include <linux/io.h> 13 #include <linux/mfd/altera-sysmgr.h> 14 #include <linux/mfd/syscon.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/of_address.h> 18 #include <linux/of_platform.h> 19 #include <linux/regmap.h> 20 #include <linux/slab.h> 21 22 /** 23 * struct altr_sysmgr - Altera SOCFPGA System Manager 24 * @regmap: the regmap used for System Manager accesses. 25 * @base : the base address for the System Manager 26 */ 27 struct altr_sysmgr { 28 struct regmap *regmap; 29 resource_size_t *base; 30 }; 31 32 static struct platform_driver altr_sysmgr_driver; 33 34 /** 35 * s10_protected_reg_write 36 * Write to a protected SMC register. 37 * @base: Base address of System Manager 38 * @reg: Address offset of register 39 * @val: Value to write 40 * Return: INTEL_SIP_SMC_STATUS_OK (0) on success 41 * INTEL_SIP_SMC_REG_ERROR on error 42 * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported 43 */ 44 static int s10_protected_reg_write(void *base, 45 unsigned int reg, unsigned int val) 46 { 47 struct arm_smccc_res result; 48 unsigned long sysmgr_base = (unsigned long)base; 49 50 arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, sysmgr_base + reg, 51 val, 0, 0, 0, 0, 0, &result); 52 53 return (int)result.a0; 54 } 55 56 /** 57 * s10_protected_reg_read 58 * Read the status of a protected SMC register 59 * @base: Base address of System Manager. 60 * @reg: Address of register 61 * @val: Value read. 62 * Return: INTEL_SIP_SMC_STATUS_OK (0) on success 63 * INTEL_SIP_SMC_REG_ERROR on error 64 * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported 65 */ 66 static int s10_protected_reg_read(void *base, 67 unsigned int reg, unsigned int *val) 68 { 69 struct arm_smccc_res result; 70 unsigned long sysmgr_base = (unsigned long)base; 71 72 arm_smccc_smc(INTEL_SIP_SMC_REG_READ, sysmgr_base + reg, 73 0, 0, 0, 0, 0, 0, &result); 74 75 *val = (unsigned int)result.a1; 76 77 return (int)result.a0; 78 } 79 80 static struct regmap_config altr_sysmgr_regmap_cfg = { 81 .name = "altr_sysmgr", 82 .reg_bits = 32, 83 .reg_stride = 4, 84 .val_bits = 32, 85 .fast_io = true, 86 .use_single_read = true, 87 .use_single_write = true, 88 }; 89 90 /** 91 * sysmgr_match_phandle 92 * Matching function used by driver_find_device(). 93 * Return: True if match is found, otherwise false. 94 */ 95 static int sysmgr_match_phandle(struct device *dev, void *data) 96 { 97 return dev->of_node == (struct device_node *)data; 98 } 99 100 /** 101 * altr_sysmgr_regmap_lookup_by_phandle 102 * Find the sysmgr previous configured in probe() and return regmap property. 103 * Return: regmap if found or error if not found. 104 */ 105 struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np, 106 const char *property) 107 { 108 struct device *dev; 109 struct altr_sysmgr *sysmgr; 110 struct device_node *sysmgr_np; 111 112 if (property) 113 sysmgr_np = of_parse_phandle(np, property, 0); 114 else 115 sysmgr_np = np; 116 117 if (!sysmgr_np) 118 return ERR_PTR(-ENODEV); 119 120 dev = driver_find_device(&altr_sysmgr_driver.driver, NULL, 121 (void *)sysmgr_np, sysmgr_match_phandle); 122 of_node_put(sysmgr_np); 123 if (!dev) 124 return ERR_PTR(-EPROBE_DEFER); 125 126 sysmgr = dev_get_drvdata(dev); 127 128 return sysmgr->regmap; 129 } 130 EXPORT_SYMBOL_GPL(altr_sysmgr_regmap_lookup_by_phandle); 131 132 static int sysmgr_probe(struct platform_device *pdev) 133 { 134 struct altr_sysmgr *sysmgr; 135 struct regmap *regmap; 136 struct resource *res; 137 struct regmap_config sysmgr_config = altr_sysmgr_regmap_cfg; 138 struct device *dev = &pdev->dev; 139 struct device_node *np = dev->of_node; 140 141 sysmgr = devm_kzalloc(dev, sizeof(*sysmgr), GFP_KERNEL); 142 if (!sysmgr) 143 return -ENOMEM; 144 145 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 146 if (!res) 147 return -ENOENT; 148 149 sysmgr_config.max_register = resource_size(res) - 150 sysmgr_config.reg_stride; 151 if (of_device_is_compatible(np, "altr,sys-mgr-s10")) { 152 /* Need physical address for SMCC call */ 153 sysmgr->base = (resource_size_t *)res->start; 154 sysmgr_config.reg_read = s10_protected_reg_read; 155 sysmgr_config.reg_write = s10_protected_reg_write; 156 157 regmap = devm_regmap_init(dev, NULL, sysmgr->base, 158 &sysmgr_config); 159 } else { 160 sysmgr->base = devm_ioremap(dev, res->start, 161 resource_size(res)); 162 if (!sysmgr->base) 163 return -ENOMEM; 164 165 sysmgr_config.max_register = res->end - res->start - 3; 166 regmap = devm_regmap_init_mmio(dev, sysmgr->base, 167 &sysmgr_config); 168 } 169 170 if (IS_ERR(regmap)) { 171 pr_err("regmap init failed\n"); 172 return PTR_ERR(regmap); 173 } 174 175 sysmgr->regmap = regmap; 176 177 platform_set_drvdata(pdev, sysmgr); 178 179 return 0; 180 } 181 182 static const struct of_device_id altr_sysmgr_of_match[] = { 183 { .compatible = "altr,sys-mgr" }, 184 { .compatible = "altr,sys-mgr-s10" }, 185 {}, 186 }; 187 MODULE_DEVICE_TABLE(of, altr_sysmgr_of_match); 188 189 static struct platform_driver altr_sysmgr_driver = { 190 .probe = sysmgr_probe, 191 .driver = { 192 .name = "altr,system_manager", 193 .of_match_table = altr_sysmgr_of_match, 194 }, 195 }; 196 197 static int __init altr_sysmgr_init(void) 198 { 199 return platform_driver_register(&altr_sysmgr_driver); 200 } 201 core_initcall(altr_sysmgr_init); 202 203 static void __exit altr_sysmgr_exit(void) 204 { 205 platform_driver_unregister(&altr_sysmgr_driver); 206 } 207 module_exit(altr_sysmgr_exit); 208 209 MODULE_AUTHOR("Thor Thayer <>"); 210 MODULE_DESCRIPTION("SOCFPGA System Manager driver"); 211 MODULE_LICENSE("GPL v2"); 212