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:
parent
fa5867cbd6
commit
198ca831a1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327215
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user