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 23struct sunxi_sram_func { 24 char *func; 25 u8 val; 26}; 27 28struct 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 37struct 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 58static 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 64static 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 70static 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 82static struct device *sram_dev; 83static LIST_HEAD(claimed_sram); 84static DEFINE_SPINLOCK(sram_lock); 85static void __iomem *base; 86 87static 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 &= sram_data->width; 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 134static int sunxi_sram_open(struct inode *inode, struct file *file) 135{ 136 return single_open(file, sunxi_sram_show, inode->i_private); 137} 138 139static 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 146static 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 151static 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 180err: 181 of_node_put(args.np); 182 return ERR_PTR(ret); 183} 184 185int 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, sram_data->offset); 212 val = readl(base + sram_data->reg); 213 val &= ~mask; 214 writel(val | ((device << sram_data->offset) & mask), 215 base + sram_data->reg); 216 217 spin_unlock(&sram_lock); 218 219 return 0; 220} 221EXPORT_SYMBOL(sunxi_sram_claim); 222 223int sunxi_sram_release(struct device *dev) 224{ 225 const struct sunxi_sram_data *sram_data; 226 struct sunxi_sram_desc *sram_desc; 227 228 if (!dev || !dev->of_node) 229 return -EINVAL; 230 231 sram_data = sunxi_sram_of_parse(dev->of_node, NULL); 232 if (IS_ERR(sram_data)) 233 return -EINVAL; 234 235 sram_desc = to_sram_desc(sram_data); 236 237 spin_lock(&sram_lock); 238 sram_desc->claimed = false; 239 spin_unlock(&sram_lock); 240 241 return 0; 242} 243EXPORT_SYMBOL(sunxi_sram_release); 244 245static int sunxi_sram_probe(struct platform_device *pdev) 246{ 247 struct resource *res; 248 struct dentry *d; 249 250 sram_dev = &pdev->dev; 251 252 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 253 base = devm_ioremap_resource(&pdev->dev, res); 254 if (IS_ERR(base)) 255 return PTR_ERR(base); 256 257 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 258 259 d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, 260 &sunxi_sram_fops); 261 if (!d) 262 return -ENOMEM; 263 264 return 0; 265} 266 267static const struct of_device_id sunxi_sram_dt_match[] = { 268 { .compatible = "allwinner,sun4i-a10-sram-controller" }, 269 { }, 270}; 271MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); 272 273static struct platform_driver sunxi_sram_driver = { 274 .driver = { 275 .name = "sunxi-sram", 276 .of_match_table = sunxi_sram_dt_match, 277 }, 278 .probe = sunxi_sram_probe, 279}; 280module_platform_driver(sunxi_sram_driver); 281 282MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 283MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); 284MODULE_LICENSE("GPL"); 285