xref: /openbmc/linux/drivers/soc/sunxi/sunxi_sram.c (revision 5e4fb642)
14af34b57SMaxime Ripard /*
24af34b57SMaxime Ripard  * Allwinner SoCs SRAM Controller Driver
34af34b57SMaxime Ripard  *
44af34b57SMaxime Ripard  * Copyright (C) 2015 Maxime Ripard
54af34b57SMaxime Ripard  *
64af34b57SMaxime Ripard  * Author: Maxime Ripard <maxime.ripard@free-electrons.com>
74af34b57SMaxime Ripard  *
84af34b57SMaxime Ripard  * This file is licensed under the terms of the GNU General Public
94af34b57SMaxime Ripard  * License version 2.  This program is licensed "as is" without any
104af34b57SMaxime Ripard  * warranty of any kind, whether express or implied.
114af34b57SMaxime Ripard  */
124af34b57SMaxime Ripard 
134af34b57SMaxime Ripard #include <linux/debugfs.h>
144af34b57SMaxime Ripard #include <linux/io.h>
154af34b57SMaxime Ripard #include <linux/module.h>
164af34b57SMaxime Ripard #include <linux/of.h>
174af34b57SMaxime Ripard #include <linux/of_address.h>
184af34b57SMaxime Ripard #include <linux/of_device.h>
194af34b57SMaxime Ripard #include <linux/platform_device.h>
204af34b57SMaxime Ripard 
214af34b57SMaxime Ripard #include <linux/soc/sunxi/sunxi_sram.h>
224af34b57SMaxime Ripard 
234af34b57SMaxime Ripard struct sunxi_sram_func {
244af34b57SMaxime Ripard 	char	*func;
254af34b57SMaxime Ripard 	u8	val;
268fed2ce9SIcenowy Zheng 	u32	reg_val;
274af34b57SMaxime Ripard };
284af34b57SMaxime Ripard 
294af34b57SMaxime Ripard struct sunxi_sram_data {
304af34b57SMaxime Ripard 	char			*name;
314af34b57SMaxime Ripard 	u8			reg;
324af34b57SMaxime Ripard 	u8			offset;
334af34b57SMaxime Ripard 	u8			width;
344af34b57SMaxime Ripard 	struct sunxi_sram_func	*func;
354af34b57SMaxime Ripard 	struct list_head	list;
364af34b57SMaxime Ripard };
374af34b57SMaxime Ripard 
384af34b57SMaxime Ripard struct sunxi_sram_desc {
394af34b57SMaxime Ripard 	struct sunxi_sram_data	data;
404af34b57SMaxime Ripard 	bool			claimed;
414af34b57SMaxime Ripard };
424af34b57SMaxime Ripard 
438fed2ce9SIcenowy Zheng #define SUNXI_SRAM_MAP(_reg_val, _val, _func)			\
444af34b57SMaxime Ripard 	{							\
454af34b57SMaxime Ripard 		.func = _func,					\
464af34b57SMaxime Ripard 		.val = _val,					\
478fed2ce9SIcenowy Zheng 		.reg_val = _reg_val,				\
484af34b57SMaxime Ripard 	}
494af34b57SMaxime Ripard 
504af34b57SMaxime Ripard #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...)		\
514af34b57SMaxime Ripard 	{							\
524af34b57SMaxime Ripard 		.name = _name,					\
534af34b57SMaxime Ripard 		.reg = _reg,					\
544af34b57SMaxime Ripard 		.offset = _off,					\
554af34b57SMaxime Ripard 		.width = _width,				\
564af34b57SMaxime Ripard 		.func = (struct sunxi_sram_func[]){		\
574af34b57SMaxime Ripard 			__VA_ARGS__, { } },			\
584af34b57SMaxime Ripard 	}
594af34b57SMaxime Ripard 
604af34b57SMaxime Ripard static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = {
614af34b57SMaxime Ripard 	.data	= SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2,
628fed2ce9SIcenowy Zheng 				  SUNXI_SRAM_MAP(0, 0, "cpu"),
638fed2ce9SIcenowy Zheng 				  SUNXI_SRAM_MAP(1, 1, "emac")),
644af34b57SMaxime Ripard };
654af34b57SMaxime Ripard 
664af34b57SMaxime Ripard static struct sunxi_sram_desc sun4i_a10_sram_d = {
674af34b57SMaxime Ripard 	.data	= SUNXI_SRAM_DATA("D", 0x4, 0x0, 1,
688fed2ce9SIcenowy Zheng 				  SUNXI_SRAM_MAP(0, 0, "cpu"),
698fed2ce9SIcenowy Zheng 				  SUNXI_SRAM_MAP(1, 1, "usb-otg")),
704af34b57SMaxime Ripard };
714af34b57SMaxime Ripard 
725e4fb642SIcenowy Zheng static struct sunxi_sram_desc sun50i_a64_sram_c = {
735e4fb642SIcenowy Zheng 	.data	= SUNXI_SRAM_DATA("C", 0x4, 24, 1,
745e4fb642SIcenowy Zheng 				  SUNXI_SRAM_MAP(0, 1, "cpu"),
755e4fb642SIcenowy Zheng 				  SUNXI_SRAM_MAP(1, 0, "de2")),
765e4fb642SIcenowy Zheng };
775e4fb642SIcenowy Zheng 
784af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_ids[] = {
794af34b57SMaxime Ripard 	{
804af34b57SMaxime Ripard 		.compatible	= "allwinner,sun4i-a10-sram-a3-a4",
814af34b57SMaxime Ripard 		.data		= &sun4i_a10_sram_a3_a4.data,
824af34b57SMaxime Ripard 	},
834af34b57SMaxime Ripard 	{
844af34b57SMaxime Ripard 		.compatible	= "allwinner,sun4i-a10-sram-d",
854af34b57SMaxime Ripard 		.data		= &sun4i_a10_sram_d.data,
864af34b57SMaxime Ripard 	},
875e4fb642SIcenowy Zheng 	{
885e4fb642SIcenowy Zheng 		.compatible	= "allwinner,sun50i-a64-sram-c",
895e4fb642SIcenowy Zheng 		.data		= &sun50i_a64_sram_c.data,
905e4fb642SIcenowy Zheng 	},
914af34b57SMaxime Ripard 	{}
924af34b57SMaxime Ripard };
934af34b57SMaxime Ripard 
944af34b57SMaxime Ripard static struct device *sram_dev;
954af34b57SMaxime Ripard static LIST_HEAD(claimed_sram);
964af34b57SMaxime Ripard static DEFINE_SPINLOCK(sram_lock);
974af34b57SMaxime Ripard static void __iomem *base;
984af34b57SMaxime Ripard 
994af34b57SMaxime Ripard static int sunxi_sram_show(struct seq_file *s, void *data)
1004af34b57SMaxime Ripard {
1014af34b57SMaxime Ripard 	struct device_node *sram_node, *section_node;
1024af34b57SMaxime Ripard 	const struct sunxi_sram_data *sram_data;
1034af34b57SMaxime Ripard 	const struct of_device_id *match;
1044af34b57SMaxime Ripard 	struct sunxi_sram_func *func;
1054af34b57SMaxime Ripard 	const __be32 *sram_addr_p, *section_addr_p;
1064af34b57SMaxime Ripard 	u32 val;
1074af34b57SMaxime Ripard 
1084af34b57SMaxime Ripard 	seq_puts(s, "Allwinner sunXi SRAM\n");
1094af34b57SMaxime Ripard 	seq_puts(s, "--------------------\n\n");
1104af34b57SMaxime Ripard 
1114af34b57SMaxime Ripard 	for_each_child_of_node(sram_dev->of_node, sram_node) {
1124af34b57SMaxime Ripard 		sram_addr_p = of_get_address(sram_node, 0, NULL, NULL);
1134af34b57SMaxime Ripard 
1144af34b57SMaxime Ripard 		seq_printf(s, "sram@%08x\n",
1154af34b57SMaxime Ripard 			   be32_to_cpu(*sram_addr_p));
1164af34b57SMaxime Ripard 
1174af34b57SMaxime Ripard 		for_each_child_of_node(sram_node, section_node) {
1184af34b57SMaxime Ripard 			match = of_match_node(sunxi_sram_dt_ids, section_node);
1194af34b57SMaxime Ripard 			if (!match)
1204af34b57SMaxime Ripard 				continue;
1214af34b57SMaxime Ripard 			sram_data = match->data;
1224af34b57SMaxime Ripard 
1234af34b57SMaxime Ripard 			section_addr_p = of_get_address(section_node, 0,
1244af34b57SMaxime Ripard 							NULL, NULL);
1254af34b57SMaxime Ripard 
1264af34b57SMaxime Ripard 			seq_printf(s, "\tsection@%04x\t(%s)\n",
1274af34b57SMaxime Ripard 				   be32_to_cpu(*section_addr_p),
1284af34b57SMaxime Ripard 				   sram_data->name);
1294af34b57SMaxime Ripard 
1304af34b57SMaxime Ripard 			val = readl(base + sram_data->reg);
1314af34b57SMaxime Ripard 			val >>= sram_data->offset;
132febe6569SJens Kuske 			val &= GENMASK(sram_data->width - 1, 0);
1334af34b57SMaxime Ripard 
1344af34b57SMaxime Ripard 			for (func = sram_data->func; func->func; func++) {
1354af34b57SMaxime Ripard 				seq_printf(s, "\t\t%s%c\n", func->func,
1368fed2ce9SIcenowy Zheng 					   func->reg_val == val ?
1378fed2ce9SIcenowy Zheng 					   '*' : ' ');
1384af34b57SMaxime Ripard 			}
1394af34b57SMaxime Ripard 		}
1404af34b57SMaxime Ripard 
1414af34b57SMaxime Ripard 		seq_puts(s, "\n");
1424af34b57SMaxime Ripard 	}
1434af34b57SMaxime Ripard 
1444af34b57SMaxime Ripard 	return 0;
1454af34b57SMaxime Ripard }
1464af34b57SMaxime Ripard 
1474af34b57SMaxime Ripard static int sunxi_sram_open(struct inode *inode, struct file *file)
1484af34b57SMaxime Ripard {
1494af34b57SMaxime Ripard 	return single_open(file, sunxi_sram_show, inode->i_private);
1504af34b57SMaxime Ripard }
1514af34b57SMaxime Ripard 
1524af34b57SMaxime Ripard static const struct file_operations sunxi_sram_fops = {
1534af34b57SMaxime Ripard 	.open = sunxi_sram_open,
1544af34b57SMaxime Ripard 	.read = seq_read,
1554af34b57SMaxime Ripard 	.llseek = seq_lseek,
1564af34b57SMaxime Ripard 	.release = single_release,
1574af34b57SMaxime Ripard };
1584af34b57SMaxime Ripard 
1594af34b57SMaxime Ripard static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data)
1604af34b57SMaxime Ripard {
1614af34b57SMaxime Ripard 	return container_of(data, struct sunxi_sram_desc, data);
1624af34b57SMaxime Ripard }
1634af34b57SMaxime Ripard 
1644af34b57SMaxime Ripard static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node,
1658fed2ce9SIcenowy Zheng 							 unsigned int *reg_value)
1664af34b57SMaxime Ripard {
1674af34b57SMaxime Ripard 	const struct of_device_id *match;
1688fed2ce9SIcenowy Zheng 	const struct sunxi_sram_data *data;
1698fed2ce9SIcenowy Zheng 	struct sunxi_sram_func *func;
1704af34b57SMaxime Ripard 	struct of_phandle_args args;
1718fed2ce9SIcenowy Zheng 	u8 val;
1724af34b57SMaxime Ripard 	int ret;
1734af34b57SMaxime Ripard 
1744af34b57SMaxime Ripard 	ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0,
1754af34b57SMaxime Ripard 					       &args);
1764af34b57SMaxime Ripard 	if (ret)
1774af34b57SMaxime Ripard 		return ERR_PTR(ret);
1784af34b57SMaxime Ripard 
1794af34b57SMaxime Ripard 	if (!of_device_is_available(args.np)) {
1804af34b57SMaxime Ripard 		ret = -EBUSY;
1814af34b57SMaxime Ripard 		goto err;
1824af34b57SMaxime Ripard 	}
1834af34b57SMaxime Ripard 
1848fed2ce9SIcenowy Zheng 	val = args.args[0];
1854af34b57SMaxime Ripard 
1864af34b57SMaxime Ripard 	match = of_match_node(sunxi_sram_dt_ids, args.np);
1874af34b57SMaxime Ripard 	if (!match) {
1884af34b57SMaxime Ripard 		ret = -EINVAL;
1894af34b57SMaxime Ripard 		goto err;
1904af34b57SMaxime Ripard 	}
1914af34b57SMaxime Ripard 
1928fed2ce9SIcenowy Zheng 	data = match->data;
1938fed2ce9SIcenowy Zheng 	if (!data) {
1948fed2ce9SIcenowy Zheng 		ret = -EINVAL;
1958fed2ce9SIcenowy Zheng 		goto err;
1968fed2ce9SIcenowy Zheng 	};
1978fed2ce9SIcenowy Zheng 
1988fed2ce9SIcenowy Zheng 	for (func = data->func; func->func; func++) {
1998fed2ce9SIcenowy Zheng 		if (val == func->val) {
2008fed2ce9SIcenowy Zheng 			if (reg_value)
2018fed2ce9SIcenowy Zheng 				*reg_value = func->reg_val;
2028fed2ce9SIcenowy Zheng 
2038fed2ce9SIcenowy Zheng 			break;
2048fed2ce9SIcenowy Zheng 		}
2058fed2ce9SIcenowy Zheng 	}
2068fed2ce9SIcenowy Zheng 
2078fed2ce9SIcenowy Zheng 	if (!func->func) {
2088fed2ce9SIcenowy Zheng 		ret = -EINVAL;
2098fed2ce9SIcenowy Zheng 		goto err;
2108fed2ce9SIcenowy Zheng 	}
2118fed2ce9SIcenowy Zheng 
2124af34b57SMaxime Ripard 	of_node_put(args.np);
2134af34b57SMaxime Ripard 	return match->data;
2144af34b57SMaxime Ripard 
2154af34b57SMaxime Ripard err:
2164af34b57SMaxime Ripard 	of_node_put(args.np);
2174af34b57SMaxime Ripard 	return ERR_PTR(ret);
2184af34b57SMaxime Ripard }
2194af34b57SMaxime Ripard 
2204af34b57SMaxime Ripard int sunxi_sram_claim(struct device *dev)
2214af34b57SMaxime Ripard {
2224af34b57SMaxime Ripard 	const struct sunxi_sram_data *sram_data;
2234af34b57SMaxime Ripard 	struct sunxi_sram_desc *sram_desc;
2244af34b57SMaxime Ripard 	unsigned int device;
2254af34b57SMaxime Ripard 	u32 val, mask;
2264af34b57SMaxime Ripard 
2274af34b57SMaxime Ripard 	if (IS_ERR(base))
2282262a65fSIcenowy Zheng 		return PTR_ERR(base);
2292262a65fSIcenowy Zheng 
2302262a65fSIcenowy Zheng 	if (!base)
2314af34b57SMaxime Ripard 		return -EPROBE_DEFER;
2324af34b57SMaxime Ripard 
2334af34b57SMaxime Ripard 	if (!dev || !dev->of_node)
2344af34b57SMaxime Ripard 		return -EINVAL;
2354af34b57SMaxime Ripard 
2364af34b57SMaxime Ripard 	sram_data = sunxi_sram_of_parse(dev->of_node, &device);
2374af34b57SMaxime Ripard 	if (IS_ERR(sram_data))
2384af34b57SMaxime Ripard 		return PTR_ERR(sram_data);
2394af34b57SMaxime Ripard 
2404af34b57SMaxime Ripard 	sram_desc = to_sram_desc(sram_data);
2414af34b57SMaxime Ripard 
2424af34b57SMaxime Ripard 	spin_lock(&sram_lock);
2434af34b57SMaxime Ripard 
2444af34b57SMaxime Ripard 	if (sram_desc->claimed) {
2454af34b57SMaxime Ripard 		spin_unlock(&sram_lock);
2464af34b57SMaxime Ripard 		return -EBUSY;
2474af34b57SMaxime Ripard 	}
2484af34b57SMaxime Ripard 
249febe6569SJens Kuske 	mask = GENMASK(sram_data->offset + sram_data->width - 1,
250febe6569SJens Kuske 		       sram_data->offset);
2514af34b57SMaxime Ripard 	val = readl(base + sram_data->reg);
2524af34b57SMaxime Ripard 	val &= ~mask;
2534af34b57SMaxime Ripard 	writel(val | ((device << sram_data->offset) & mask),
2544af34b57SMaxime Ripard 	       base + sram_data->reg);
2554af34b57SMaxime Ripard 
2564af34b57SMaxime Ripard 	spin_unlock(&sram_lock);
2574af34b57SMaxime Ripard 
2584af34b57SMaxime Ripard 	return 0;
2594af34b57SMaxime Ripard }
2604af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_claim);
2614af34b57SMaxime Ripard 
2624af34b57SMaxime Ripard int sunxi_sram_release(struct device *dev)
2634af34b57SMaxime Ripard {
2644af34b57SMaxime Ripard 	const struct sunxi_sram_data *sram_data;
2654af34b57SMaxime Ripard 	struct sunxi_sram_desc *sram_desc;
2664af34b57SMaxime Ripard 
2674af34b57SMaxime Ripard 	if (!dev || !dev->of_node)
2684af34b57SMaxime Ripard 		return -EINVAL;
2694af34b57SMaxime Ripard 
2704af34b57SMaxime Ripard 	sram_data = sunxi_sram_of_parse(dev->of_node, NULL);
2714af34b57SMaxime Ripard 	if (IS_ERR(sram_data))
2724af34b57SMaxime Ripard 		return -EINVAL;
2734af34b57SMaxime Ripard 
2744af34b57SMaxime Ripard 	sram_desc = to_sram_desc(sram_data);
2754af34b57SMaxime Ripard 
2764af34b57SMaxime Ripard 	spin_lock(&sram_lock);
2774af34b57SMaxime Ripard 	sram_desc->claimed = false;
2784af34b57SMaxime Ripard 	spin_unlock(&sram_lock);
2794af34b57SMaxime Ripard 
2804af34b57SMaxime Ripard 	return 0;
2814af34b57SMaxime Ripard }
2824af34b57SMaxime Ripard EXPORT_SYMBOL(sunxi_sram_release);
2834af34b57SMaxime Ripard 
2844af34b57SMaxime Ripard static int sunxi_sram_probe(struct platform_device *pdev)
2854af34b57SMaxime Ripard {
2864af34b57SMaxime Ripard 	struct resource *res;
2874af34b57SMaxime Ripard 	struct dentry *d;
2884af34b57SMaxime Ripard 
2894af34b57SMaxime Ripard 	sram_dev = &pdev->dev;
2904af34b57SMaxime Ripard 
2914af34b57SMaxime Ripard 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2924af34b57SMaxime Ripard 	base = devm_ioremap_resource(&pdev->dev, res);
2934af34b57SMaxime Ripard 	if (IS_ERR(base))
2944af34b57SMaxime Ripard 		return PTR_ERR(base);
2954af34b57SMaxime Ripard 
2964af34b57SMaxime Ripard 	of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
2974af34b57SMaxime Ripard 
2984af34b57SMaxime Ripard 	d = debugfs_create_file("sram", S_IRUGO, NULL, NULL,
2994af34b57SMaxime Ripard 				&sunxi_sram_fops);
3004af34b57SMaxime Ripard 	if (!d)
3014af34b57SMaxime Ripard 		return -ENOMEM;
3024af34b57SMaxime Ripard 
3034af34b57SMaxime Ripard 	return 0;
3044af34b57SMaxime Ripard }
3054af34b57SMaxime Ripard 
3064af34b57SMaxime Ripard static const struct of_device_id sunxi_sram_dt_match[] = {
3074af34b57SMaxime Ripard 	{ .compatible = "allwinner,sun4i-a10-sram-controller" },
3085e4fb642SIcenowy Zheng 	{ .compatible = "allwinner,sun50i-a64-sram-controller" },
3094af34b57SMaxime Ripard 	{ },
3104af34b57SMaxime Ripard };
3114af34b57SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);
3124af34b57SMaxime Ripard 
3134af34b57SMaxime Ripard static struct platform_driver sunxi_sram_driver = {
3144af34b57SMaxime Ripard 	.driver = {
3154af34b57SMaxime Ripard 		.name		= "sunxi-sram",
3164af34b57SMaxime Ripard 		.of_match_table	= sunxi_sram_dt_match,
3174af34b57SMaxime Ripard 	},
3184af34b57SMaxime Ripard 	.probe	= sunxi_sram_probe,
3194af34b57SMaxime Ripard };
3204af34b57SMaxime Ripard module_platform_driver(sunxi_sram_driver);
3214af34b57SMaxime Ripard 
3224af34b57SMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
3234af34b57SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver");
3244af34b57SMaxime Ripard MODULE_LICENSE("GPL");
325