162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
462306a36Sopenharmony_ci *                    <benh@kernel.crashing.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#ifndef _ASM_POWERPC_DCR_NATIVE_H
862306a36Sopenharmony_ci#define _ASM_POWERPC_DCR_NATIVE_H
962306a36Sopenharmony_ci#ifdef __KERNEL__
1062306a36Sopenharmony_ci#ifndef __ASSEMBLY__
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/spinlock.h>
1362306a36Sopenharmony_ci#include <asm/cputable.h>
1462306a36Sopenharmony_ci#include <asm/cpu_has_feature.h>
1562306a36Sopenharmony_ci#include <linux/stringify.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_citypedef struct {
1862306a36Sopenharmony_ci	unsigned int base;
1962306a36Sopenharmony_ci} dcr_host_native_t;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic inline bool dcr_map_ok_native(dcr_host_native_t host)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	return true;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define dcr_map_native(dev, dcr_n, dcr_c) \
2762306a36Sopenharmony_ci	((dcr_host_native_t){ .base = (dcr_n) })
2862306a36Sopenharmony_ci#define dcr_unmap_native(host, dcr_c)		do {} while (0)
2962306a36Sopenharmony_ci#define dcr_read_native(host, dcr_n)		mfdcr(dcr_n + host.base)
3062306a36Sopenharmony_ci#define dcr_write_native(host, dcr_n, value)	mtdcr(dcr_n + host.base, value)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Table based DCR accessors */
3362306a36Sopenharmony_ciextern void __mtdcr(unsigned int reg, unsigned int val);
3462306a36Sopenharmony_ciextern unsigned int __mfdcr(unsigned int reg);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* mfdcrx/mtdcrx instruction based accessors. We hand code
3762306a36Sopenharmony_ci * the opcodes in order not to depend on newer binutils
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic inline unsigned int mfdcrx(unsigned int reg)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned int ret;
4262306a36Sopenharmony_ci	asm volatile(".long 0x7c000206 | (%0 << 21) | (%1 << 16)"
4362306a36Sopenharmony_ci		     : "=r" (ret) : "r" (reg));
4462306a36Sopenharmony_ci	return ret;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline void mtdcrx(unsigned int reg, unsigned int val)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	asm volatile(".long 0x7c000306 | (%0 << 21) | (%1 << 16)"
5062306a36Sopenharmony_ci		     : : "r" (val), "r" (reg));
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define mfdcr(rn)						\
5462306a36Sopenharmony_ci	({unsigned int rval;					\
5562306a36Sopenharmony_ci	if (__builtin_constant_p(rn) && rn < 1024)		\
5662306a36Sopenharmony_ci		asm volatile("mfdcr %0, %1" : "=r" (rval)	\
5762306a36Sopenharmony_ci			      : "n" (rn));			\
5862306a36Sopenharmony_ci	else if (likely(cpu_has_feature(CPU_FTR_INDEXED_DCR)))	\
5962306a36Sopenharmony_ci		rval = mfdcrx(rn);				\
6062306a36Sopenharmony_ci	else							\
6162306a36Sopenharmony_ci		rval = __mfdcr(rn);				\
6262306a36Sopenharmony_ci	rval;})
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define mtdcr(rn, v)						\
6562306a36Sopenharmony_cido {								\
6662306a36Sopenharmony_ci	if (__builtin_constant_p(rn) && rn < 1024)		\
6762306a36Sopenharmony_ci		asm volatile("mtdcr %0, %1"			\
6862306a36Sopenharmony_ci			      : : "n" (rn), "r" (v));		\
6962306a36Sopenharmony_ci	else if (likely(cpu_has_feature(CPU_FTR_INDEXED_DCR)))	\
7062306a36Sopenharmony_ci		mtdcrx(rn, v);					\
7162306a36Sopenharmony_ci	else							\
7262306a36Sopenharmony_ci		__mtdcr(rn, v);					\
7362306a36Sopenharmony_ci} while (0)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* R/W of indirect DCRs make use of standard naming conventions for DCRs */
7662306a36Sopenharmony_ciextern spinlock_t dcr_ind_lock;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic inline unsigned __mfdcri(int base_addr, int base_data, int reg)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned long flags;
8162306a36Sopenharmony_ci	unsigned int val;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock_irqsave(&dcr_ind_lock, flags);
8462306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
8562306a36Sopenharmony_ci		mtdcrx(base_addr, reg);
8662306a36Sopenharmony_ci		val = mfdcrx(base_data);
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		__mtdcr(base_addr, reg);
8962306a36Sopenharmony_ci		val = __mfdcr(base_data);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dcr_ind_lock, flags);
9262306a36Sopenharmony_ci	return val;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline void __mtdcri(int base_addr, int base_data, int reg,
9662306a36Sopenharmony_ci			    unsigned val)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unsigned long flags;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	spin_lock_irqsave(&dcr_ind_lock, flags);
10162306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
10262306a36Sopenharmony_ci		mtdcrx(base_addr, reg);
10362306a36Sopenharmony_ci		mtdcrx(base_data, val);
10462306a36Sopenharmony_ci	} else {
10562306a36Sopenharmony_ci		__mtdcr(base_addr, reg);
10662306a36Sopenharmony_ci		__mtdcr(base_data, val);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dcr_ind_lock, flags);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline void __dcri_clrset(int base_addr, int base_data, int reg,
11262306a36Sopenharmony_ci				 unsigned clr, unsigned set)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	unsigned long flags;
11562306a36Sopenharmony_ci	unsigned int val;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	spin_lock_irqsave(&dcr_ind_lock, flags);
11862306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
11962306a36Sopenharmony_ci		mtdcrx(base_addr, reg);
12062306a36Sopenharmony_ci		val = (mfdcrx(base_data) & ~clr) | set;
12162306a36Sopenharmony_ci		mtdcrx(base_data, val);
12262306a36Sopenharmony_ci	} else {
12362306a36Sopenharmony_ci		__mtdcr(base_addr, reg);
12462306a36Sopenharmony_ci		val = (__mfdcr(base_data) & ~clr) | set;
12562306a36Sopenharmony_ci		__mtdcr(base_data, val);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dcr_ind_lock, flags);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define mfdcri(base, reg)	__mfdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
13162306a36Sopenharmony_ci					 DCRN_ ## base ## _CONFIG_DATA,	\
13262306a36Sopenharmony_ci					 reg)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define mtdcri(base, reg, data)	__mtdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
13562306a36Sopenharmony_ci					 DCRN_ ## base ## _CONFIG_DATA,	\
13662306a36Sopenharmony_ci					 reg, data)
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define dcri_clrset(base, reg, clr, set)	__dcri_clrset(DCRN_ ## base ## _CONFIG_ADDR,	\
13962306a36Sopenharmony_ci							      DCRN_ ## base ## _CONFIG_DATA,	\
14062306a36Sopenharmony_ci							      reg, clr, set)
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */
14362306a36Sopenharmony_ci#endif /* __KERNEL__ */
14462306a36Sopenharmony_ci#endif /* _ASM_POWERPC_DCR_NATIVE_H */
145