extres/syscon: Commit missing bits from r327106

r327106 introduced kobj to syscon so it can be subclassed and fit in with
the rest of the syscon framework. The diff for syscon.c was misapplied in a
clean tree prior to commit, so bring it back to what was included in the
review and tested. The entire file has basically been rewritten from what
was present prior to the kobj work.

Pointy hat to:	me
This commit is contained in:
Kyle Evans 2017-12-26 16:38:04 +00:00
parent fa5867cbd6
commit 198ca831a1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327215

View File

@ -33,153 +33,223 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/kobj.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/sx.h>
#include <sys/queue.h>
#include <machine/bus.h>
#ifdef FDT
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#endif
#include "syscon_if.h"
#include "syscon.h"
#define SYSCON_LOCK(_sc) mtx_lock(&(_sc)->mtx)
#define SYSCON_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
#define SYSCON_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \
device_get_nameunit((_sc)->dev), "syscon", MTX_DEF)
#define SYSCON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx);
#define SYSCON_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED);
#define SYSCON_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
/*
* Syscon interface details
*/
typedef TAILQ_HEAD(syscon_list, syscon) syscon_list_t;
struct syscon_softc {
device_t dev;
struct resource *mem_res;
struct mtx mtx;
/*
* Declarations
*/
static int syscon_method_init(struct syscon *syscon);
static int syscon_method_uninit(struct syscon *syscon);
MALLOC_DEFINE(M_SYSCON, "syscon", "Syscon driver");
static syscon_list_t syscon_list = TAILQ_HEAD_INITIALIZER(syscon_list);
static struct sx syscon_topo_lock;
SX_SYSINIT(syscon_topology, &syscon_topo_lock, "Syscon topology lock");
/*
* Syscon methods.
*/
static syscon_method_t syscon_methods[] = {
SYSCONMETHOD(syscon_init, syscon_method_init),
SYSCONMETHOD(syscon_uninit, syscon_method_uninit),
SYSCONMETHOD_END
};
DEFINE_CLASS_0(syscon, syscon_class, syscon_methods, 0);
#define SYSCON_TOPO_SLOCK() sx_slock(&syscon_topo_lock)
#define SYSCON_TOPO_XLOCK() sx_xlock(&syscon_topo_lock)
#define SYSCON_TOPO_UNLOCK() sx_unlock(&syscon_topo_lock)
#define SYSCON_TOPO_ASSERT() sx_assert(&syscon_topo_lock, SA_LOCKED)
#define SYSCON_TOPO_XASSERT() sx_assert(&syscon_topo_lock, SA_XLOCKED)
/*
* Default syscon methods for base class.
*/
static int
syscon_method_init(struct syscon *syscon)
{
return (0);
};
static struct ofw_compat_data compat_data[] = {
{"syscon", 1},
{NULL, 0}
static int
syscon_method_uninit(struct syscon *syscon)
{
return (0);
};
static uint32_t
syscon_read_4(device_t dev, device_t consumer, bus_size_t offset)
void *
syscon_get_softc(struct syscon *syscon)
{
struct syscon_softc *sc;
uint32_t val;
sc = device_get_softc(dev);
return (syscon->softc);
};
SYSCON_LOCK(sc);
val = bus_read_4(sc->mem_res, offset);
SYSCON_UNLOCK(sc);
return (val);
/*
* Create and initialize syscon object, but do not register it.
*/
struct syscon *
syscon_create(device_t pdev, syscon_class_t syscon_class)
{
struct syscon *syscon;
/* Create object and initialize it. */
syscon = malloc(sizeof(struct syscon), M_SYSCON,
M_WAITOK | M_ZERO);
kobj_init((kobj_t)syscon, (kobj_class_t)syscon_class);
/* Allocate softc if required. */
if (syscon_class->size > 0)
syscon->softc = malloc(syscon_class->size, M_SYSCON,
M_WAITOK | M_ZERO);
/* Rest of init. */
syscon->pdev = pdev;
return (syscon);
}
static void
syscon_write_4(device_t dev, device_t consumer, bus_size_t offset, uint32_t val)
/* Register syscon object. */
struct syscon *
syscon_register(struct syscon *syscon)
{
struct syscon_softc *sc;
int rv;
sc = device_get_softc(dev);
#ifdef FDT
if (syscon->ofw_node <= 0)
syscon->ofw_node = ofw_bus_get_node(syscon->pdev);
if (syscon->ofw_node <= 0)
return (NULL);
#endif
SYSCON_LOCK(sc);
bus_write_4(sc->mem_res, offset, val);
SYSCON_UNLOCK(sc);
}
static void
syscon_modify_4(device_t dev, device_t consumer, bus_size_t offset,
uint32_t clear_bits, uint32_t set_bits)
{
struct syscon_softc *sc;
uint32_t val;
sc = device_get_softc(dev);
SYSCON_LOCK(sc);
val = bus_read_4(sc->mem_res, offset);
val &= ~clear_bits;
val |= set_bits;
bus_write_4(sc->mem_res, offset, val);
SYSCON_UNLOCK(sc);
}
static int
syscon_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
device_set_desc(dev, "syscon");
return (BUS_PROBE_GENERIC);
}
static int
syscon_attach(device_t dev)
{
struct syscon_softc *sc;
int rid;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(sc->dev);
SYSCON_LOCK_INIT(sc);
rid = 0;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->mem_res == NULL) {
device_printf(dev, "Cannot allocate memory resource\n");
return (ENXIO);
rv = SYSCON_INIT(syscon);
if (rv != 0) {
printf("SYSCON_INIT failed: %d\n", rv);
return (NULL);
}
OF_device_register_xref(OF_xref_from_node(node), dev);
return (0);
#ifdef FDT
OF_device_register_xref(OF_xref_from_node(syscon->ofw_node),
syscon->pdev);
#endif
SYSCON_TOPO_XLOCK();
TAILQ_INSERT_TAIL(&syscon_list, syscon, syscon_link);
SYSCON_TOPO_UNLOCK();
return (syscon);
}
static int
syscon_detach(device_t dev)
int
syscon_unregister(struct syscon *syscon)
{
struct syscon_softc *sc;
sc = device_get_softc(dev);
OF_device_register_xref(OF_xref_from_device(dev), NULL);
SYSCON_LOCK_DESTROY(sc);
if (sc->mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
return (0);
SYSCON_TOPO_XLOCK();
TAILQ_REMOVE(&syscon_list, syscon, syscon_link);
SYSCON_TOPO_UNLOCK();
#ifdef FDT
OF_device_register_xref(OF_xref_from_node(syscon->ofw_node), NULL);
#endif
return (SYSCON_UNINIT(syscon));
}
static device_method_t syscon_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, syscon_probe),
DEVMETHOD(device_attach, syscon_attach),
DEVMETHOD(device_detach, syscon_detach),
/**
* Provider methods
*/
#ifdef FDT
static struct syscon *
syscon_find_by_ofw_node(phandle_t node)
{
struct syscon *entry;
/* Syscon interface */
DEVMETHOD(syscon_read_4, syscon_read_4),
DEVMETHOD(syscon_write_4, syscon_write_4),
DEVMETHOD(syscon_modify_4, syscon_modify_4),
SYSCON_TOPO_ASSERT();
DEVMETHOD_END
};
TAILQ_FOREACH(entry, &syscon_list, syscon_link) {
if (entry->ofw_node == node)
return (entry);
}
DEFINE_CLASS_0(syscon, syscon_driver, syscon_methods,
sizeof(struct syscon_softc));
static devclass_t syscon_devclass;
EARLY_DRIVER_MODULE(syscon, simplebus, syscon_driver, syscon_devclass, 0, 0,
BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
MODULE_VERSION(syscon, 1);
return (NULL);
}
struct syscon *
syscon_create_ofw_node(device_t pdev, syscon_class_t syscon_class,
phandle_t node)
{
struct syscon *syscon;
syscon = syscon_create(pdev, syscon_class);
if (syscon == NULL)
return (NULL);
syscon->ofw_node = node;
if (syscon_register(syscon) == NULL)
return (NULL);
return (syscon);
}
phandle_t
syscon_get_ofw_node(struct syscon *syscon)
{
return (syscon->ofw_node);
}
int
syscon_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name,
struct syscon **syscon)
{
pcell_t *cells;
int ncells;
if (cnode <= 0)
cnode = ofw_bus_get_node(cdev);
if (cnode <= 0) {
device_printf(cdev,
"%s called on not ofw based device\n", __func__);
return (ENXIO);
}
ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t),
(void **)&cells);
if (ncells < 1)
return (ENXIO);
/* Translate to syscon node. */
SYSCON_TOPO_SLOCK();
*syscon = syscon_find_by_ofw_node(OF_node_from_xref(cells[0]));
if (*syscon == NULL) {
SYSCON_TOPO_UNLOCK();
device_printf(cdev, "Failed to find syscon node\n");
OF_prop_free(cells);
return (ENODEV);
}
SYSCON_TOPO_UNLOCK();
OF_prop_free(cells);
return (0);
}
#endif