Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfd2f0d4 authored by Andrew Donnellan's avatar Andrew Donnellan Committed by Michael Ellerman
Browse files

powerpc/powernv: Get rid of old scom_controller abstraction



Once upon a time, the SCOM access code was used by the WSP platform as
well as powernv. Thus it made sense to have a generic SCOM access
interface to abstract between different platforms.

Now that it's just powernv, with no other platforms currently on the
horizon, let's rip out scom_controller and make everything much
simpler and more direct.

While we're here, fix up the comment block at the top.

Signed-off-by: default avatarAndrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190509051119.7694-3-ajd@linux.ibm.com
parent 9edc6b91
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ obj-y += idle.o opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
obj-y			+= rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
obj-y			+= opal-msglog.o opal-hmi.o opal-power.o opal-irqchip.o
obj-y			+= opal-kmsg.o opal-powercap.o opal-psr.o opal-sensor-groups.o
obj-y			+= opal-xscom.o scom.o

obj-$(CONFIG_SMP)	+= smp.o subcore.o subcore-asm.o
obj-$(CONFIG_PCI)	+= pci.o pci-ioda.o npu-dma.o pci-ioda-tce.o
@@ -16,3 +15,4 @@ obj-$(CONFIG_PERF_EVENTS) += opal-imc.o
obj-$(CONFIG_PPC_MEMTRACE)	+= memtrace.o
obj-$(CONFIG_PPC_VAS)	+= vas.o vas-window.o vas-debug.o
obj-$(CONFIG_OCXL_BASE)	+= ocxl.o
obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
+141 −73
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * PowerNV LPC bus handling.
 * PowerNV SCOM bus debugfs interface
 *
 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
 *                <benh@kernel.crashing.org>
 *     and        David Gibson, IBM Corporation.
 * Copyright 2013 IBM Corp.
 */

@@ -10,63 +13,13 @@
#include <linux/bug.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#include <asm/machdep.h>
#include <asm/firmware.h>
#include <asm/opal.h>

#include "scom.h"

/*
 * We could probably fit that inside the scom_map_t
 * which is a void* after all but it's really too ugly
 * so let's kmalloc it for now
 */
struct opal_scom_map {
	uint32_t chip;
	uint64_t addr;
};

static scom_map_t opal_scom_map(struct device_node *dev, u64 reg, u64 count)
{
	struct opal_scom_map *m;
	const __be32 *gcid;

	if (!of_get_property(dev, "scom-controller", NULL)) {
		pr_err("%s: device %pOF is not a SCOM controller\n",
			__func__, dev);
		return SCOM_MAP_INVALID;
	}
	gcid = of_get_property(dev, "ibm,chip-id", NULL);
	if (!gcid) {
		pr_err("%s: device %pOF has no ibm,chip-id\n",
			__func__, dev);
		return SCOM_MAP_INVALID;
	}
	m = kmalloc(sizeof(*m), GFP_KERNEL);
	if (!m)
		return NULL;
	m->chip = be32_to_cpup(gcid);
	m->addr = reg;

	return (scom_map_t)m;
}

static void opal_scom_unmap(scom_map_t map)
{
	kfree(map);
}

static int opal_xscom_err_xlate(int64_t rc)
{
	switch(rc) {
	case 0:
		return 0;
	/* Add more translations if necessary */
	default:
		return -EIO;
	}
}
#include <asm/debugfs.h>
#include <asm/prom.h>

static u64 opal_scom_unmangle(u64 addr)
{
@@ -99,39 +52,154 @@ static u64 opal_scom_unmangle(u64 addr)
	return addr;
}

static int opal_scom_read(scom_map_t map, u64 reg, u64 *value)
static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
{
	struct opal_scom_map *m = map;
	int64_t rc;
	__be64 v;

	reg = opal_scom_unmangle(m->addr + reg);
	rc = opal_xscom_read(m->chip, reg, (__be64 *)__pa(&v));
	reg = opal_scom_unmangle(addr + reg);
	rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
	if (rc) {
		*value = 0xfffffffffffffffful;
		return -EIO;
	}
	*value = be64_to_cpu(v);
	return opal_xscom_err_xlate(rc);
	return 0;
}

static int opal_scom_write(scom_map_t map, u64 reg, u64 value)
static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
{
	struct opal_scom_map *m = map;
	int64_t rc;

	reg = opal_scom_unmangle(m->addr + reg);
	rc = opal_xscom_write(m->chip, reg, value);
	return opal_xscom_err_xlate(rc);
	reg = opal_scom_unmangle(addr + reg);
	rc = opal_xscom_write(chip, reg, value);
	if (rc)
		return -EIO;
	return 0;
}

struct scom_debug_entry {
	u32 chip;
	struct debugfs_blob_wrapper path;
	char name[16];
};

static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
			       size_t count, loff_t *ppos)
{
	struct scom_debug_entry *ent = filp->private_data;
	u64 __user *ubuf64 = (u64 __user *)ubuf;
	loff_t off = *ppos;
	ssize_t done = 0; 
	u64 reg, reg_base, reg_cnt, val;
	int rc;

	if (off < 0 || (off & 7) || (count & 7))
		return -EINVAL;
	reg_base = off >> 3;
	reg_cnt = count >> 3;

	for (reg = 0; reg < reg_cnt; reg++) {
		rc = opal_scom_read(ent->chip, reg_base, reg, &val);
		if (!rc)
			rc = put_user(val, ubuf64);
		if (rc) {
			if (!done)
				done = rc;
			break;
		}
		ubuf64++;
		*ppos += 8;
		done += 8;
	}
	return done;
}

static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf,
				size_t count, loff_t *ppos)
{
	struct scom_debug_entry *ent = filp->private_data;
	u64 __user *ubuf64 = (u64 __user *)ubuf;
	loff_t off = *ppos;
	ssize_t done = 0; 
	u64 reg, reg_base, reg_cnt, val;
	int rc;

	if (off < 0 || (off & 7) || (count & 7))
		return -EINVAL;
	reg_base = off >> 3;
	reg_cnt = count >> 3;

	for (reg = 0; reg < reg_cnt; reg++) {
		rc = get_user(val, ubuf64);
		if (!rc)
			rc = opal_scom_write(ent->chip, reg_base, reg,  val);
		if (rc) {
			if (!done)
				done = rc;
			break;
		}
		ubuf64++;
		done += 8;
	}
	return done;
}

static const struct scom_controller opal_scom_controller = {
	.map	= opal_scom_map,
	.unmap	= opal_scom_unmap,
	.read	= opal_scom_read,
	.write	= opal_scom_write
static const struct file_operations scom_debug_fops = {
	.read =		scom_debug_read,
	.write =	scom_debug_write,
	.open =		simple_open,
	.llseek =	default_llseek,
};

static int opal_xscom_init(void)
static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
			       int chip)
{
	if (firmware_has_feature(FW_FEATURE_OPAL))
		scom_init(&opal_scom_controller);
	struct scom_debug_entry *ent;
	struct dentry *dir;

	ent = kzalloc(sizeof(*ent), GFP_KERNEL);
	if (!ent)
		return -ENOMEM;

	ent->chip = chip;
	snprintf(ent->name, 16, "%08x", chip);
	ent->path.data = (void*)kasprintf(GFP_KERNEL, "%pOF", dn);
	ent->path.size = strlen((char *)ent->path.data);

	dir = debugfs_create_dir(ent->name, root);
	if (!dir) {
		kfree(ent->path.data);
		kfree(ent);
		return -1;
	}

	debugfs_create_blob("devspec", 0400, dir, &ent->path);
	debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);

	return 0;
}

static int scom_debug_init(void)
{
	struct device_node *dn;
	struct dentry *root;
	int chip, rc;

	if (!firmware_has_feature(FW_FEATURE_OPAL))
		return 0;

	root = debugfs_create_dir("scom", powerpc_debugfs_root);
	if (!root)
		return -1;

	rc = 0;
	for_each_node_with_property(dn, "scom-controller") {
		chip = of_get_ibm_chip_id(dn);
		WARN_ON(chip == -1);
		rc |= scom_debug_init_one(root, dn, chip);
	}

	return rc;
}
machine_arch_initcall(powernv, opal_xscom_init);
device_initcall(scom_debug_init);
+0 −157
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
 *                <benh@kernel.crashing.org>
 *     and        David Gibson, IBM Corporation.
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <asm/debugfs.h>
#include <asm/prom.h>
#include <linux/uaccess.h>

#include "scom.h"

const struct scom_controller *scom_controller;

#ifdef CONFIG_SCOM_DEBUGFS
struct scom_debug_entry {
	struct device_node *dn;
	struct debugfs_blob_wrapper path;
	char name[16];
};

static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
			       size_t count, loff_t *ppos)
{
	struct scom_debug_entry *ent = filp->private_data;
	u64 __user *ubuf64 = (u64 __user *)ubuf;
	loff_t off = *ppos;
	ssize_t done = 0; 
	u64 reg, reg_cnt, val;
	scom_map_t map;
	int rc;

	if (off < 0 || (off & 7) || (count & 7))
		return -EINVAL;
	reg = off >> 3;
	reg_cnt = count >> 3;

	map = scom_map(ent->dn, reg, reg_cnt);
	if (!scom_map_ok(map))
		return -ENXIO;

	for (reg = 0; reg < reg_cnt; reg++) {
		rc = scom_read(map, reg, &val);
		if (!rc)
			rc = put_user(val, ubuf64);
		if (rc) {
			if (!done)
				done = rc;
			break;
		}
		ubuf64++;
		*ppos += 8;
		done += 8;
	}
	scom_unmap(map);
	return done;
}

static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf,
				size_t count, loff_t *ppos)
{
	struct scom_debug_entry *ent = filp->private_data;
	u64 __user *ubuf64 = (u64 __user *)ubuf;
	loff_t off = *ppos;
	ssize_t done = 0; 
	u64 reg, reg_cnt, val;
	scom_map_t map;
	int rc;

	if (off < 0 || (off & 7) || (count & 7))
		return -EINVAL;
	reg = off >> 3;
	reg_cnt = count >> 3;

	map = scom_map(ent->dn, reg, reg_cnt);
	if (!scom_map_ok(map))
		return -ENXIO;

	for (reg = 0; reg < reg_cnt; reg++) {
		rc = get_user(val, ubuf64);
		if (!rc)
			rc = scom_write(map, reg,  val);
		if (rc) {
			if (!done)
				done = rc;
			break;
		}
		ubuf64++;
		done += 8;
	}
	scom_unmap(map);
	return done;
}

static const struct file_operations scom_debug_fops = {
	.read =		scom_debug_read,
	.write =	scom_debug_write,
	.open =		simple_open,
	.llseek =	default_llseek,
};

static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
			       int i)
{
	struct scom_debug_entry *ent;
	struct dentry *dir;

	ent = kzalloc(sizeof(*ent), GFP_KERNEL);
	if (!ent)
		return -ENOMEM;

	ent->dn = of_node_get(dn);
	snprintf(ent->name, 16, "%08x", i);
	ent->path.data = (void*)kasprintf(GFP_KERNEL, "%pOF", dn);
	ent->path.size = strlen((char *)ent->path.data);

	dir = debugfs_create_dir(ent->name, root);
	if (!dir) {
		of_node_put(dn);
		kfree(ent->path.data);
		kfree(ent);
		return -1;
	}

	debugfs_create_blob("devspec", 0400, dir, &ent->path);
	debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);

	return 0;
}

static int scom_debug_init(void)
{
	struct device_node *dn;
	struct dentry *root;
	int i, rc;

	root = debugfs_create_dir("scom", powerpc_debugfs_root);
	if (!root)
		return -1;

	i = rc = 0;
	for_each_node_with_property(dn, "scom-controller") {
		int id = of_get_ibm_chip_id(dn);
		if (id == -1)
			id = i;
		rc |= scom_debug_init_one(root, dn, id);
		i++;
	}

	return rc;
}
device_initcall(scom_debug_init);
#endif /* CONFIG_SCOM_DEBUGFS */
+0 −120
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
 *                <benh@kernel.crashing.org>
 *     and        David Gibson, IBM Corporation.
 */

#ifndef _SCOM_H
#define _SCOM_H

/*
 * The SCOM bus is a sideband bus used for accessing various internal
 * registers of the processor or the chipset. The implementation details
 * differ between processors and platforms, and the access method as
 * well.
 *
 * This API allows to "map" ranges of SCOM register numbers associated
 * with a given SCOM controller. The later must be represented by a
 * device node, though some implementations might support NULL if there
 * is no possible ambiguity
 *
 * Then, scom_read/scom_write can be used to accesses registers inside
 * that range. The argument passed is a register number relative to
 * the beginning of the range mapped.
 */

typedef void *scom_map_t;

/* Value for an invalid SCOM map */
#define SCOM_MAP_INVALID	(NULL)

/* The scom_controller data structure is what the platform passes
 * to the core code in scom_init, it provides the actual implementation
 * of all the SCOM functions
 */
struct scom_controller {
	scom_map_t (*map)(struct device_node *ctrl_dev, u64 reg, u64 count);
	void (*unmap)(scom_map_t map);

	int (*read)(scom_map_t map, u64 reg, u64 *value);
	int (*write)(scom_map_t map, u64 reg, u64 value);
};

extern const struct scom_controller *scom_controller;

/**
 * scom_init - Initialize the SCOM backend, called by the platform
 * @controller: The platform SCOM controller
 */
static inline void scom_init(const struct scom_controller *controller)
{
	scom_controller = controller;
}

/**
 * scom_map_ok - Test is a SCOM mapping is successful
 * @map: The result of scom_map to test
 */
static inline int scom_map_ok(scom_map_t map)
{
	return map != SCOM_MAP_INVALID;
}

/**
 * scom_map - Map a block of SCOM registers
 * @ctrl_dev: Device node of the SCOM controller
 *            some implementations allow NULL here
 * @reg: first SCOM register to map
 * @count: Number of SCOM registers to map
 */

static inline scom_map_t scom_map(struct device_node *ctrl_dev,
				  u64 reg, u64 count)
{
	return scom_controller->map(ctrl_dev, reg, count);
}

/**
 * scom_unmap - Unmap a block of SCOM registers
 * @map: Result of scom_map is to be unmapped
 */
static inline void scom_unmap(scom_map_t map)
{
	if (scom_map_ok(map))
		scom_controller->unmap(map);
}

/**
 * scom_read - Read a SCOM register
 * @map: Result of scom_map
 * @reg: Register index within that map
 * @value: Updated with the value read
 *
 * Returns 0 (success) or a negative error code
 */
static inline int scom_read(scom_map_t map, u64 reg, u64 *value)
{
	int rc;

	rc = scom_controller->read(map, reg, value);
	if (rc)
		*value = 0xfffffffffffffffful;
	return rc;
}

/**
 * scom_write - Write to a SCOM register
 * @map: Result of scom_map
 * @reg: Register index within that map
 * @value: Value to write
 *
 * Returns 0 (success) or a negative error code
 */
static inline int scom_write(scom_map_t map, u64 reg, u64 value)
{
	return scom_controller->write(map, reg, value);
}


#endif /* _SCOM_H */