1 /* 2 * Allwinner SoCs SRAM Controller Driver 3 * 4 * Copyright (C) 2015 Maxime Ripard 5 * 6 * Author: Maxime Ripard <maxime.ripard@free-electrons.com> 7 * 8 * This file is licensed under the terms of the GNU General Public 9 * License version 2. This program is licensed "as is" without any 10 * warranty of any kind, whether express or implied. 11 */ 12 13 #include <linux/debugfs.h> 14 #include <linux/io.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/of_address.h> 18 #include <linux/of_device.h> 19 #include <linux/platform_device.h> 20 21 #include <linux/soc/sunxi/sunxi_sram.h> 22 23 struct sunxi_sram_func { 24 char *func; 25 u8 val; 26 }; 27 28 struct sunxi_sram_data { 29 char *name; 30 u8 reg; 31 u8 offset; 32 u8 width; 33 struct sunxi_sram_func *func; 34 struct list_head list; 35 }; 36 37 struct sunxi_sram_desc { 38 struct sunxi_sram_data data; 39 bool claimed; 40 }; 41 42 #define SUNXI_SRAM_MAP(_val, _func) \ 43 { \ 44 .func = _func, \ 45 .val = _val, \ 46 } 47 48 #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...) \ 49 { \ 50 .name = _name, \ 51 .reg = _reg, \ 52 .offset = _off, \ 53 .width = _width, \ 54 .func = (struct sunxi_sram_func[]){ \ 55 __VA_ARGS__, { } }, \ 56 } 57 58 static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = { 59 .data = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2, 60 SUNXI_SRAM_MAP(0, "cpu"), 61 SUNXI_SRAM_MAP(1, "emac")), 62 }; 63 64 static struct sunxi_sram_desc sun4i_a10_sram_d = { 65 .data = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1, 66 SUNXI_SRAM_MAP(0, "cpu"), 67 SUNXI_SRAM_MAP(1, "usb-otg")), 68 }; 69 70 static const struct of_device_id sunxi_sram_dt_ids[] = { 71 { 72 .compatible = "allwinner,sun4i-a10-sram-a3-a4", 73 .data = &sun4i_a10_sram_a3_a4.data, 74 }, 75 { 76 .compatible = "allwinner,sun4i-a10-sram-d", 77 .data = &sun4i_a10_sram_d.data, 78 }, 79 {} 80 }; 81 82 static struct device *sram_dev; 83 static LIST_HEAD(claimed_sram); 84 static DEFINE_SPINLOCK(sram_lock); 85 static void __iomem *base; 86 87 static int sunxi_sram_show(struct seq_file *s, void *data) 88 { 89 struct device_node *sram_node, *section_node; 90 const struct sunxi_sram_data *sram_data; 91 const struct of_device_id *match; 92 struct sunxi_sram_func *func; 93 const __be32 *sram_addr_p, *section_addr_p; 94 u32 val; 95 96 seq_puts(s, "Allwinner sunXi SRAM\n"); 97 seq_puts(s, "--------------------\n\n"); 98 99 for_each_child_of_node(sram_dev->of_node, sram_node) { 100 sram_addr_p = of_get_address(sram_node, 0, NULL, NULL); 101 102 seq_printf(s, "sram@%08x\n", 103 be32_to_cpu(*sram_addr_p)); 104 105 for_each_child_of_node(sram_node, section_node) { 106 match = of_match_node(sunxi_sram_dt_ids, section_node); 107 if (!match) 108 continue; 109 sram_data = match->data; 110 111 section_addr_p = of_get_address(section_node, 0, 112 NULL, NULL); 113 114 seq_printf(s, "\tsection@%04x\t(%s)\n", 115 be32_to_cpu(*section_addr_p), 116 sram_data->name); 117 118 val = readl(base + sram_data->reg); 119 val >>= sram_data->offset; 120 val &= GENMASK(sram_data->width - 1, 0); 121 122 for (func = sram_data->func; func->func; func++) { 123 seq_printf(s, "\t\t%s%c\n", func->func, 124 func->val == val ? '*' : ' '); 125 } 126 } 127 128 seq_puts(s, "\n"); 129 } 130 131 return 0; 132 } 133 134 static int sunxi_sram_open(struct inode *inode, struct file *file) 135 { 136 return single_open(file, sunxi_sram_show, inode->i_private); 137 } 138 139 static const struct file_operations sunxi_sram_fops = { 140 .open = sunxi_sram_open, 141 .read = seq_read, 142 .llseek = seq_lseek, 143 .release = single_release, 144 }; 145 146 static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) 147 { 148 return container_of(data, struct sunxi_sram_desc, data); 149 } 150 151 static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node, 152 unsigned int *value) 153 { 154 const struct of_device_id *match; 155 struct of_phandle_args args; 156 int ret; 157 158 ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0, 159 &args); 160 if (ret) 161 return ERR_PTR(ret); 162 163 if (!of_device_is_available(args.np)) { 164 ret = -EBUSY; 165 goto err; 166 } 167 168 if (value) 169 *value = args.args[0]; 170 171 match = of_match_node(sunxi_sram_dt_ids, args.np); 172 if (!match) { 173 ret = -EINVAL; 174 goto err; 175 } 176 177 of_node_put(args.np); 178 return match->data; 179 180 err: 181 of_node_put(args.np); 182 return ERR_PTR(ret); 183 } 184 185 int sunxi_sram_claim(struct device *dev) 186 { 187 const struct sunxi_sram_data *sram_data; 188 struct sunxi_sram_desc *sram_desc; 189 unsigned int device; 190 u32 val, mask; 191 192 if (IS_ERR(base)) 193 return -EPROBE_DEFER; 194 195 if (!dev || !dev->of_node) 196 return -EINVAL; 197 198 sram_data = sunxi_sram_of_parse(dev->of_node, &device); 199 if (IS_ERR(sram_data)) 200 return PTR_ERR(sram_data); 201 202 sram_desc = to_sram_desc(sram_data); 203 204 spin_lock(&sram_lock); 205 206 if (sram_desc->claimed) { 207 spin_unlock(&sram_lock); 208 return -EBUSY; 209 } 210 211 mask = GENMASK(sram_data->offset + sram_data->width - 1, 212 sram_data->offset); 213 val = readl(base + sram_data->reg); 214 val &= ~mask; 215 writel(val | ((device << sram_data->offset) & mask), 216 base + sram_data->reg); 217 218 spin_unlock(&sram_lock); 219 220 return 0; 221 } 222 EXPORT_SYMBOL(sunxi_sram_claim); 223 224 int sunxi_sram_release(struct device *dev) 225 { 226 const struct sunxi_sram_data *sram_data; 227 struct sunxi_sram_desc *sram_desc; 228 229 if (!dev || !dev->of_node) 230 return -EINVAL; 231 232 sram_data = sunxi_sram_of_parse(dev->of_node, NULL); 233 if (IS_ERR(sram_data)) 234 return -EINVAL; 235 236 sram_desc = to_sram_desc(sram_data); 237 238 spin_lock(&sram_lock); 239 sram_desc->claimed = false; 240 spin_unlock(&sram_lock); 241 242 return 0; 243 } 244 EXPORT_SYMBOL(sunxi_sram_release); 245 246 static int sunxi_sram_probe(struct platform_device *pdev) 247 { 248 struct resource *res; 249 struct dentry *d; 250 251 sram_dev = &pdev->dev; 252 253 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 254 base = devm_ioremap_resource(&pdev->dev, res); 255 if (IS_ERR(base)) 256 return PTR_ERR(base); 257 258 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 259 260 d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, 261 &sunxi_sram_fops); 262 if (!d) 263 return -ENOMEM; 264 265 return 0; 266 } 267 268 static const struct of_device_id sunxi_sram_dt_match[] = { 269 { .compatible = "allwinner,sun4i-a10-sram-controller" }, 270 { }, 271 }; 272 MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); 273 274 static struct platform_driver sunxi_sram_driver = { 275 .driver = { 276 .name = "sunxi-sram", 277 .of_match_table = sunxi_sram_dt_match, 278 }, 279 .probe = sunxi_sram_probe, 280 }; 281 module_platform_driver(sunxi_sram_driver); 282 283 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 284 MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); 285 MODULE_LICENSE("GPL"); 286