Skip to content

Commit

Permalink
hw/ocmb: Add OCMB SCOM support
Browse files Browse the repository at this point in the history
Add a driver for the SCOM ranges of the OCMB. Unlike most chips the OCMB
has two different (three if you count OpenCAPI config space) register
spaces and we need to ensure that the right access size is used on each.
Additionally the SCOM interface is a bit non-standard in that a full
physical address is passed as the SCOM address rather than a register
number so we don't need to perform any address transformations, we just
need to verify that the address falls into one of the nominated address
ranges.

Cc: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
  • Loading branch information
oohal committed Apr 8, 2020
1 parent 38b5c31 commit e991415
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
4 changes: 4 additions & 0 deletions core/init.c
Expand Up @@ -29,6 +29,7 @@
#include <console.h>
#include <fsi-master.h>
#include <centaur.h>
#include <ocmb.h>
#include <libfdt/libfdt.h>
#include <timer.h>
#include <ipmi.h>
Expand Down Expand Up @@ -1190,6 +1191,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
/* Grab centaurs from device-tree if present (only on FSP-less) */
centaur_init();

/* initialize ocmb scom-controller */
ocmb_init();

/* Initialize PSI (depends on probe_platform being called) */
psi_init();

Expand Down
1 change: 1 addition & 0 deletions hw/Makefile.inc
Expand Up @@ -9,6 +9,7 @@ HW_OBJS += fake-nvram.o lpc-mbox.o npu2.o npu2-hw-procedures.o
HW_OBJS += npu2-common.o npu2-opencapi.o phys-map.o sbe-p9.o capp.o
HW_OBJS += occ-sensor.o vas.o sbe-p8.o dio-p9.o lpc-port80h.o cache-p9.o
HW_OBJS += npu-opal.o npu3.o npu3-nvlink.o npu3-hw-procedures.o
HW_OBJS += ocmb.o
HW=hw/built-in.a

include $(SRC)/hw/fsp/Makefile.inc
Expand Down
167 changes: 167 additions & 0 deletions hw/ocmb.c
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Open Capi Memory Buffer chip
*
* Copyright 2020 IBM Corp.
*/


#define pr_fmt(fmt) "OCMB: " fmt

#include <skiboot.h>
#include <xscom.h>
#include <device.h>
#include <ocmb.h>
#include <io.h>
#include <inttypes.h>

struct ocmb_range {
uint64_t start;
uint64_t end;
uint64_t flags;

/* flags come from hdat */
#define ACCESS_8B PPC_BIT(0)
#define ACCESS_4B PPC_BIT(1)
#define ACCESS_SIZE_MASK (ACCESS_8B | ACCESS_4B)
};

struct ocmb {
struct scom_controller scom;
int range_count;
struct ocmb_range ranges[];
};

static const struct ocmb_range *find_range(const struct ocmb *o, uint64_t offset)
{
int i;

for (i = 0; i < o->range_count; i++) {
uint64_t start = o->ranges[i].start;
uint64_t end = o->ranges[i].end;

if (offset >= start && offset <= end)
return &o->ranges[i];
}

return NULL;
}

static int64_t ocmb_fake_scom_write(struct scom_controller *f,
uint32_t __unused chip_id,
uint64_t offset, uint64_t val)
{
const struct ocmb *o = f->private;
const struct ocmb_range *r;

r = find_range(o, offset);
if (!r) {
prerror("no matching address range!\n");
return OPAL_XSCOM_ADDR_ERROR;
}

switch (r->flags & ACCESS_SIZE_MASK) {
case ACCESS_8B:
if (offset & 0x7)
return OPAL_XSCOM_ADDR_ERROR;
out_be64((void *) offset, val);
break;

case ACCESS_4B:
if (offset & 0x3)
return OPAL_XSCOM_ADDR_ERROR;
out_be32((void *) offset, val);
break;
default:
prerror("bad flags? %llx\n", r->flags);
return OPAL_XSCOM_ADDR_ERROR;
}

return OPAL_SUCCESS;
}

static int64_t ocmb_fake_scom_read(struct scom_controller *f,
uint32_t chip_id __unused,
uint64_t offset, uint64_t *val)
{
const struct ocmb *o = f->private;
const struct ocmb_range *r = NULL;

r = find_range(o, offset);
if (!r) {
prerror("no matching address range!\n");
return OPAL_XSCOM_ADDR_ERROR;
}


switch (r->flags & ACCESS_SIZE_MASK) {
case ACCESS_8B:
if (offset & 0x7)
return OPAL_XSCOM_ADDR_ERROR;
*val = in_be64((void *) offset);
break;

case ACCESS_4B:
if (offset & 0x3)
return OPAL_XSCOM_ADDR_ERROR;
*val = in_be32((void *) offset);
break;
default:
prerror("bad flags? %llx\n", r->flags);
return OPAL_XSCOM_ADDR_ERROR;
}

return OPAL_SUCCESS;
}

static bool ocmb_probe_one(struct dt_node *ocmb_node)
{
uint64_t chip_id = dt_prop_get_u32(ocmb_node, "ibm,chip-id");
const struct dt_property *flags;
int i = 0, num = 0;
struct ocmb *ocmb;

num = dt_count_addresses(ocmb_node);

ocmb = zalloc(sizeof(*ocmb) + sizeof(*ocmb->ranges) * num);
if (!ocmb)
return false;

ocmb->scom.private = ocmb;
ocmb->scom.part_id = chip_id;
ocmb->scom.write = ocmb_fake_scom_write;
ocmb->scom.read = ocmb_fake_scom_read;
ocmb->range_count = num;

flags = dt_require_property(ocmb_node, "flags", sizeof(u64) * num);

for (i = 0; i < num; i++) {
uint64_t start, size;

start = dt_get_address(ocmb_node, i, &size);

ocmb->ranges[i].start = start;
ocmb->ranges[i].end = start + size - 1;
ocmb->ranges[i].flags = dt_property_get_u64(flags, i);

prlog(PR_DEBUG, "Added range: %" PRIx64 " - [%llx - %llx]\n",
chip_id, start, start + size - 1);
}

if (scom_register(&ocmb->scom))
prerror("error registienr fake socm\n");

dt_add_property(ocmb_node, "scom-controller", NULL, 0);

prerror("XXX: Added scom controller for %s\n", ocmb_node->name);

return true;
}

void ocmb_init(void)
{
struct dt_node *dn;

dt_for_each_compatible(dt_root, dn, "ibm,explorer")
ocmb_probe_one(dn);
}
13 changes: 13 additions & 0 deletions include/ocmb.h
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Open Capi Memory Buffer chip
*
* Copyright 2020 IBM Corp.
*/

#ifndef __OCMB_H
#define __OCMB_H

extern void ocmb_init(void);

#endif /* __OCMB_H */

0 comments on commit e991415

Please sign in to comment.