18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/sh/kernel/cpu/sh4/sq.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * General management API for SH-4 integrated Store Queues
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2006  Paul Mundt
88c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002  M. R. Brown
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/cpu.h>
128c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
218c2ecf20Sopenharmony_ci#include <asm/page.h>
228c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
238c2ecf20Sopenharmony_ci#include <cpu/sq.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct sq_mapping;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct sq_mapping {
288c2ecf20Sopenharmony_ci	const char *name;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	unsigned long sq_addr;
318c2ecf20Sopenharmony_ci	unsigned long addr;
328c2ecf20Sopenharmony_ci	unsigned int size;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	struct sq_mapping *next;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct sq_mapping *sq_mapping_list;
388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sq_mapping_lock);
398c2ecf20Sopenharmony_cistatic struct kmem_cache *sq_cache;
408c2ecf20Sopenharmony_cistatic unsigned long *sq_bitmap;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define store_queue_barrier()			\
438c2ecf20Sopenharmony_cido {						\
448c2ecf20Sopenharmony_ci	(void)__raw_readl(P4SEG_STORE_QUE);	\
458c2ecf20Sopenharmony_ci	__raw_writel(0, P4SEG_STORE_QUE + 0);	\
468c2ecf20Sopenharmony_ci	__raw_writel(0, P4SEG_STORE_QUE + 8);	\
478c2ecf20Sopenharmony_ci} while (0);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/**
508c2ecf20Sopenharmony_ci * sq_flush_range - Flush (prefetch) a specific SQ range
518c2ecf20Sopenharmony_ci * @start: the store queue address to start flushing from
528c2ecf20Sopenharmony_ci * @len: the length to flush
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Flushes the store queue cache from @start to @start + @len in a
558c2ecf20Sopenharmony_ci * linear fashion.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_civoid sq_flush_range(unsigned long start, unsigned int len)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned long *sq = (unsigned long *)start;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* Flush the queues */
628c2ecf20Sopenharmony_ci	for (len >>= 5; len--; sq += 8)
638c2ecf20Sopenharmony_ci		prefetchw(sq);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* Wait for completion */
668c2ecf20Sopenharmony_ci	store_queue_barrier();
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sq_flush_range);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic inline void sq_mapping_list_add(struct sq_mapping *map)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct sq_mapping **p, *tmp;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	spin_lock_irq(&sq_mapping_lock);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	p = &sq_mapping_list;
778c2ecf20Sopenharmony_ci	while ((tmp = *p) != NULL)
788c2ecf20Sopenharmony_ci		p = &tmp->next;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	map->next = tmp;
818c2ecf20Sopenharmony_ci	*p = map;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	spin_unlock_irq(&sq_mapping_lock);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic inline void sq_mapping_list_del(struct sq_mapping *map)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct sq_mapping **p, *tmp;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	spin_lock_irq(&sq_mapping_lock);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	for (p = &sq_mapping_list; (tmp = *p); p = &tmp->next)
938c2ecf20Sopenharmony_ci		if (tmp == map) {
948c2ecf20Sopenharmony_ci			*p = tmp->next;
958c2ecf20Sopenharmony_ci			break;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	spin_unlock_irq(&sq_mapping_lock);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int __sq_remap(struct sq_mapping *map, pgprot_t prot)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci#if defined(CONFIG_MMU)
1048c2ecf20Sopenharmony_ci	struct vm_struct *vma;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	vma = __get_vm_area_caller(map->size, VM_ALLOC, map->sq_addr,
1078c2ecf20Sopenharmony_ci			SQ_ADDRMAX, __builtin_return_address(0));
1088c2ecf20Sopenharmony_ci	if (!vma)
1098c2ecf20Sopenharmony_ci		return -ENOMEM;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	vma->phys_addr = map->addr;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (ioremap_page_range((unsigned long)vma->addr,
1148c2ecf20Sopenharmony_ci			       (unsigned long)vma->addr + map->size,
1158c2ecf20Sopenharmony_ci			       vma->phys_addr, prot)) {
1168c2ecf20Sopenharmony_ci		vunmap(vma->addr);
1178c2ecf20Sopenharmony_ci		return -EAGAIN;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci#else
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 * Without an MMU (or with it turned off), this is much more
1228c2ecf20Sopenharmony_ci	 * straightforward, as we can just load up each queue's QACR with
1238c2ecf20Sopenharmony_ci	 * the physical address appropriately masked.
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci	__raw_writel(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0);
1268c2ecf20Sopenharmony_ci	__raw_writel(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1);
1278c2ecf20Sopenharmony_ci#endif
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * sq_remap - Map a physical address through the Store Queues
1348c2ecf20Sopenharmony_ci * @phys: Physical address of mapping.
1358c2ecf20Sopenharmony_ci * @size: Length of mapping.
1368c2ecf20Sopenharmony_ci * @name: User invoking mapping.
1378c2ecf20Sopenharmony_ci * @prot: Protection bits.
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Remaps the physical address @phys through the next available store queue
1408c2ecf20Sopenharmony_ci * address of @size length. @name is logged at boot time as well as through
1418c2ecf20Sopenharmony_ci * the sysfs interface.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_ciunsigned long sq_remap(unsigned long phys, unsigned int size,
1448c2ecf20Sopenharmony_ci		       const char *name, pgprot_t prot)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct sq_mapping *map;
1478c2ecf20Sopenharmony_ci	unsigned long end;
1488c2ecf20Sopenharmony_ci	unsigned int psz;
1498c2ecf20Sopenharmony_ci	int ret, page;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Don't allow wraparound or zero size */
1528c2ecf20Sopenharmony_ci	end = phys + size - 1;
1538c2ecf20Sopenharmony_ci	if (unlikely(!size || end < phys))
1548c2ecf20Sopenharmony_ci		return -EINVAL;
1558c2ecf20Sopenharmony_ci	/* Don't allow anyone to remap normal memory.. */
1568c2ecf20Sopenharmony_ci	if (unlikely(phys < virt_to_phys(high_memory)))
1578c2ecf20Sopenharmony_ci		return -EINVAL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	phys &= PAGE_MASK;
1608c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(end + 1) - phys;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	map = kmem_cache_alloc(sq_cache, GFP_KERNEL);
1638c2ecf20Sopenharmony_ci	if (unlikely(!map))
1648c2ecf20Sopenharmony_ci		return -ENOMEM;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	map->addr = phys;
1678c2ecf20Sopenharmony_ci	map->size = size;
1688c2ecf20Sopenharmony_ci	map->name = name;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	page = bitmap_find_free_region(sq_bitmap, 0x04000000 >> PAGE_SHIFT,
1718c2ecf20Sopenharmony_ci				       get_order(map->size));
1728c2ecf20Sopenharmony_ci	if (unlikely(page < 0)) {
1738c2ecf20Sopenharmony_ci		ret = -ENOSPC;
1748c2ecf20Sopenharmony_ci		goto out;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	map->sq_addr = P4SEG_STORE_QUE + (page << PAGE_SHIFT);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = __sq_remap(map, prot);
1808c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
1818c2ecf20Sopenharmony_ci		goto out;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	psz = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
1848c2ecf20Sopenharmony_ci	pr_info("sqremap: %15s  [%4d page%s]  va 0x%08lx   pa 0x%08lx\n",
1858c2ecf20Sopenharmony_ci		likely(map->name) ? map->name : "???",
1868c2ecf20Sopenharmony_ci		psz, psz == 1 ? " " : "s",
1878c2ecf20Sopenharmony_ci		map->sq_addr, map->addr);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	sq_mapping_list_add(map);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return map->sq_addr;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciout:
1948c2ecf20Sopenharmony_ci	kmem_cache_free(sq_cache, map);
1958c2ecf20Sopenharmony_ci	return ret;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sq_remap);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * sq_unmap - Unmap a Store Queue allocation
2018c2ecf20Sopenharmony_ci * @vaddr: Pre-allocated Store Queue mapping.
2028c2ecf20Sopenharmony_ci *
2038c2ecf20Sopenharmony_ci * Unmaps the store queue allocation @map that was previously created by
2048c2ecf20Sopenharmony_ci * sq_remap(). Also frees up the pte that was previously inserted into
2058c2ecf20Sopenharmony_ci * the kernel page table and discards the UTLB translation.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_civoid sq_unmap(unsigned long vaddr)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct sq_mapping **p, *map;
2108c2ecf20Sopenharmony_ci	int page;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	for (p = &sq_mapping_list; (map = *p); p = &map->next)
2138c2ecf20Sopenharmony_ci		if (map->sq_addr == vaddr)
2148c2ecf20Sopenharmony_ci			break;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (unlikely(!map)) {
2178c2ecf20Sopenharmony_ci		printk("%s: bad store queue address 0x%08lx\n",
2188c2ecf20Sopenharmony_ci		       __func__, vaddr);
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	page = (map->sq_addr - P4SEG_STORE_QUE) >> PAGE_SHIFT;
2238c2ecf20Sopenharmony_ci	bitmap_release_region(sq_bitmap, page, get_order(map->size));
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU
2268c2ecf20Sopenharmony_ci	{
2278c2ecf20Sopenharmony_ci		/*
2288c2ecf20Sopenharmony_ci		 * Tear down the VMA in the MMU case.
2298c2ecf20Sopenharmony_ci		 */
2308c2ecf20Sopenharmony_ci		struct vm_struct *vma;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		vma = remove_vm_area((void *)(map->sq_addr & PAGE_MASK));
2338c2ecf20Sopenharmony_ci		if (!vma) {
2348c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: bad address 0x%08lx\n",
2358c2ecf20Sopenharmony_ci			       __func__, map->sq_addr);
2368c2ecf20Sopenharmony_ci			return;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci#endif
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	sq_mapping_list_del(map);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	kmem_cache_free(sq_cache, map);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sq_unmap);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/*
2488c2ecf20Sopenharmony_ci * Needlessly complex sysfs interface. Unfortunately it doesn't seem like
2498c2ecf20Sopenharmony_ci * there is any other easy way to add things on a per-cpu basis without
2508c2ecf20Sopenharmony_ci * putting the directory entries somewhere stupid and having to create
2518c2ecf20Sopenharmony_ci * links in sysfs by hand back in to the per-cpu directories.
2528c2ecf20Sopenharmony_ci *
2538c2ecf20Sopenharmony_ci * Some day we may want to have an additional abstraction per store
2548c2ecf20Sopenharmony_ci * queue, but considering the kobject hell we already have to deal with,
2558c2ecf20Sopenharmony_ci * it's simply not worth the trouble.
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_cistatic struct kobject *sq_kobject[NR_CPUS];
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistruct sq_sysfs_attr {
2608c2ecf20Sopenharmony_ci	struct attribute attr;
2618c2ecf20Sopenharmony_ci	ssize_t (*show)(char *buf);
2628c2ecf20Sopenharmony_ci	ssize_t (*store)(const char *buf, size_t count);
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#define to_sq_sysfs_attr(a)	container_of(a, struct sq_sysfs_attr, attr)
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr,
2688c2ecf20Sopenharmony_ci			     char *buf)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (likely(sattr->show))
2738c2ecf20Sopenharmony_ci		return sattr->show(buf);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return -EIO;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic ssize_t sq_sysfs_store(struct kobject *kobj, struct attribute *attr,
2798c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (likely(sattr->store))
2848c2ecf20Sopenharmony_ci		return sattr->store(buf, count);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return -EIO;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic ssize_t mapping_show(char *buf)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct sq_mapping **list, *entry;
2928c2ecf20Sopenharmony_ci	char *p = buf;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	for (list = &sq_mapping_list; (entry = *list); list = &entry->next)
2958c2ecf20Sopenharmony_ci		p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n",
2968c2ecf20Sopenharmony_ci			     entry->sq_addr, entry->sq_addr + entry->size,
2978c2ecf20Sopenharmony_ci			     entry->addr, entry->name);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return p - buf;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic ssize_t mapping_store(const char *buf, size_t count)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	unsigned long base = 0, len = 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	sscanf(buf, "%lx %lx", &base, &len);
3078c2ecf20Sopenharmony_ci	if (!base)
3088c2ecf20Sopenharmony_ci		return -EIO;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (likely(len)) {
3118c2ecf20Sopenharmony_ci		int ret = sq_remap(base, len, "Userspace", PAGE_SHARED);
3128c2ecf20Sopenharmony_ci		if (ret < 0)
3138c2ecf20Sopenharmony_ci			return ret;
3148c2ecf20Sopenharmony_ci	} else
3158c2ecf20Sopenharmony_ci		sq_unmap(base);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return count;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic struct sq_sysfs_attr mapping_attr =
3218c2ecf20Sopenharmony_ci	__ATTR(mapping, 0644, mapping_show, mapping_store);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic struct attribute *sq_sysfs_attrs[] = {
3248c2ecf20Sopenharmony_ci	&mapping_attr.attr,
3258c2ecf20Sopenharmony_ci	NULL,
3268c2ecf20Sopenharmony_ci};
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic const struct sysfs_ops sq_sysfs_ops = {
3298c2ecf20Sopenharmony_ci	.show	= sq_sysfs_show,
3308c2ecf20Sopenharmony_ci	.store	= sq_sysfs_store,
3318c2ecf20Sopenharmony_ci};
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic struct kobj_type ktype_percpu_entry = {
3348c2ecf20Sopenharmony_ci	.sysfs_ops	= &sq_sysfs_ops,
3358c2ecf20Sopenharmony_ci	.default_attrs	= sq_sysfs_attrs,
3368c2ecf20Sopenharmony_ci};
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int sq_dev_add(struct device *dev, struct subsys_interface *sif)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	unsigned int cpu = dev->id;
3418c2ecf20Sopenharmony_ci	struct kobject *kobj;
3428c2ecf20Sopenharmony_ci	int error;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	sq_kobject[cpu] = kzalloc(sizeof(struct kobject), GFP_KERNEL);
3458c2ecf20Sopenharmony_ci	if (unlikely(!sq_kobject[cpu]))
3468c2ecf20Sopenharmony_ci		return -ENOMEM;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	kobj = sq_kobject[cpu];
3498c2ecf20Sopenharmony_ci	error = kobject_init_and_add(kobj, &ktype_percpu_entry, &dev->kobj,
3508c2ecf20Sopenharmony_ci				     "%s", "sq");
3518c2ecf20Sopenharmony_ci	if (!error)
3528c2ecf20Sopenharmony_ci		kobject_uevent(kobj, KOBJ_ADD);
3538c2ecf20Sopenharmony_ci	return error;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void sq_dev_remove(struct device *dev, struct subsys_interface *sif)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	unsigned int cpu = dev->id;
3598c2ecf20Sopenharmony_ci	struct kobject *kobj = sq_kobject[cpu];
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	kobject_put(kobj);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic struct subsys_interface sq_interface = {
3658c2ecf20Sopenharmony_ci	.name		= "sq",
3668c2ecf20Sopenharmony_ci	.subsys		= &cpu_subsys,
3678c2ecf20Sopenharmony_ci	.add_dev	= sq_dev_add,
3688c2ecf20Sopenharmony_ci	.remove_dev	= sq_dev_remove,
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int __init sq_api_init(void)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	unsigned int nr_pages = 0x04000000 >> PAGE_SHIFT;
3748c2ecf20Sopenharmony_ci	unsigned int size = (nr_pages + (BITS_PER_LONG - 1)) / BITS_PER_LONG;
3758c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "sq: Registering store queue API.\n");
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	sq_cache = kmem_cache_create("store_queue_cache",
3808c2ecf20Sopenharmony_ci				sizeof(struct sq_mapping), 0, 0, NULL);
3818c2ecf20Sopenharmony_ci	if (unlikely(!sq_cache))
3828c2ecf20Sopenharmony_ci		return ret;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	sq_bitmap = kcalloc(size, sizeof(long), GFP_KERNEL);
3858c2ecf20Sopenharmony_ci	if (unlikely(!sq_bitmap))
3868c2ecf20Sopenharmony_ci		goto out;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	ret = subsys_interface_register(&sq_interface);
3898c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
3908c2ecf20Sopenharmony_ci		goto out;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ciout:
3958c2ecf20Sopenharmony_ci	kfree(sq_bitmap);
3968c2ecf20Sopenharmony_ci	kmem_cache_destroy(sq_cache);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return ret;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void __exit sq_api_exit(void)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	subsys_interface_unregister(&sq_interface);
4048c2ecf20Sopenharmony_ci	kfree(sq_bitmap);
4058c2ecf20Sopenharmony_ci	kmem_cache_destroy(sq_cache);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cimodule_init(sq_api_init);
4098c2ecf20Sopenharmony_cimodule_exit(sq_api_exit);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>");
4128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues");
4138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
414