Refine locking inside of syscon driver.

In some cases, the syscon driver may be used by consumer requiring better
control about locking (ie. it may be used as registe file provider for clock
driver which needs locked access to multiple registers).
Add fine locking protocol methods together with bunch of helper functions
in syscon driver and implement this functionality in syscon_generic driver.

MFC after:	4 weeks
This commit is contained in:
Michal Meloun 2020-09-25 16:44:01 +00:00
parent 0d46edd0bb
commit 01d0f9c0e4
3 changed files with 140 additions and 31 deletions

View File

@ -67,6 +67,12 @@ typedef TAILQ_HEAD(syscon_list, syscon) syscon_list_t;
*/
static int syscon_method_init(struct syscon *syscon);
static int syscon_method_uninit(struct syscon *syscon);
static uint32_t syscon_method_read_4(struct syscon *syscon, bus_size_t offset);
static int syscon_method_write_4(struct syscon *syscon, bus_size_t offset,
uint32_t val);
static int syscon_method_modify_4(struct syscon *syscon, bus_size_t offset,
uint32_t clear_bits, uint32_t set_bits);
MALLOC_DEFINE(M_SYSCON, "syscon", "Syscon driver");
@ -80,6 +86,9 @@ SX_SYSINIT(syscon_topology, &syscon_topo_lock, "Syscon topology lock");
static syscon_method_t syscon_methods[] = {
SYSCONMETHOD(syscon_init, syscon_method_init),
SYSCONMETHOD(syscon_uninit, syscon_method_uninit),
SYSCONMETHOD(syscon_read_4, syscon_method_read_4),
SYSCONMETHOD(syscon_write_4, syscon_method_write_4),
SYSCONMETHOD(syscon_modify_4, syscon_method_modify_4),
SYSCONMETHOD_END
};
@ -115,6 +124,39 @@ syscon_get_softc(struct syscon *syscon)
return (syscon->softc);
};
static uint32_t
syscon_method_read_4(struct syscon *syscon, bus_size_t offset)
{
uint32_t val;
SYSCON_DEVICE_LOCK(syscon->pdev);
val = SYSCON_UNLOCKED_READ_4(syscon, offset);
SYSCON_DEVICE_UNLOCK(syscon->pdev);
return(val);
}
static int
syscon_method_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val)
{
int rv;
SYSCON_DEVICE_LOCK(syscon->pdev);
rv = SYSCON_UNLOCKED_WRITE_4(syscon, offset, val);
SYSCON_DEVICE_UNLOCK(syscon->pdev);
return(rv);
}
static int
syscon_method_modify_4(struct syscon *syscon, bus_size_t offset,
uint32_t clear_bits, uint32_t set_bits)
{
int rv;
SYSCON_DEVICE_LOCK(syscon->pdev);
rv = SYSCON_UNLOCKED_MODIFY_4(syscon, offset, clear_bits, set_bits);
SYSCON_DEVICE_UNLOCK(syscon->pdev);
return(rv);
}
/*
* Create and initialize syscon object, but do not register it.
*/
@ -254,14 +296,3 @@ syscon_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name,
return (0);
}
#endif
int
syscon_get_handle_default(device_t dev, struct syscon **syscon)
{
device_t parent;
parent = device_get_parent(dev);
if (parent == NULL)
return (ENODEV);
return (SYSCON_GET_HANDLE(parent, syscon));
}

View File

@ -55,11 +55,12 @@ __FBSDID("$FreeBSD$");
MALLOC_DECLARE(M_SYSCON);
static uint32_t syscon_generic_read_4(struct syscon *syscon, bus_size_t offset);
static int syscon_generic_write_4(struct syscon *syscon, bus_size_t offset,
uint32_t val);
static int syscon_generic_modify_4(struct syscon *syscon, bus_size_t offset,
uint32_t clear_bits, uint32_t set_bits);
static uint32_t syscon_generic_unlocked_read_4(struct syscon *syscon,
bus_size_t offset);
static int syscon_generic_unlocked_write_4(struct syscon *syscon,
bus_size_t offset, uint32_t val);
static int syscon_generic_unlocked_modify_4(struct syscon *syscon,
bus_size_t offset, uint32_t clear_bits, uint32_t set_bits);
static int syscon_generic_detach(device_t dev);
/*
* Generic syscon driver (FDT)
@ -78,9 +79,9 @@ static struct ofw_compat_data compat_data[] = {
#define SYSCON_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
static syscon_method_t syscon_generic_methods[] = {
SYSCONMETHOD(syscon_read_4, syscon_generic_read_4),
SYSCONMETHOD(syscon_write_4, syscon_generic_write_4),
SYSCONMETHOD(syscon_modify_4, syscon_generic_modify_4),
SYSCONMETHOD(syscon_unlocked_read_4, syscon_generic_unlocked_read_4),
SYSCONMETHOD(syscon_unlocked_write_4, syscon_generic_unlocked_write_4),
SYSCONMETHOD(syscon_unlocked_modify_4, syscon_generic_unlocked_modify_4),
SYSCONMETHOD_END
};
@ -88,50 +89,62 @@ DEFINE_CLASS_1(syscon_generic, syscon_generic_class, syscon_generic_methods,
0, syscon_class);
static uint32_t
syscon_generic_read_4(struct syscon *syscon, bus_size_t offset)
syscon_generic_unlocked_read_4(struct syscon *syscon, bus_size_t offset)
{
struct syscon_generic_softc *sc;
uint32_t val;
sc = device_get_softc(syscon->pdev);
SYSCON_LOCK(sc);
SYSCON_ASSERT_UNLOCKED(sc);
val = bus_read_4(sc->mem_res, offset);
SYSCON_UNLOCK(sc);
return (val);
}
static int
syscon_generic_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val)
syscon_generic_unlocked_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val)
{
struct syscon_generic_softc *sc;
sc = device_get_softc(syscon->pdev);
SYSCON_LOCK(sc);
SYSCON_ASSERT_UNLOCKED(sc);
bus_write_4(sc->mem_res, offset, val);
SYSCON_UNLOCK(sc);
return (0);
}
static int
syscon_generic_modify_4(struct syscon *syscon, bus_size_t offset,
syscon_generic_unlocked_modify_4(struct syscon *syscon, bus_size_t offset,
uint32_t clear_bits, uint32_t set_bits)
{
struct syscon_generic_softc *sc;
uint32_t val;
sc = device_get_softc(syscon->pdev);
SYSCON_LOCK(sc);
SYSCON_ASSERT_UNLOCKED(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);
return (0);
}
static void
syscon_generic_lock(device_t dev)
{
struct syscon_generic_softc *sc;
sc = device_get_softc(dev);
SYSCON_LOCK(sc);
}
static void
syscon_generic_unlock(device_t dev)
{
struct syscon_generic_softc *sc;
sc = device_get_softc(dev);
SYSCON_UNLOCK(sc);
}
static int
syscon_generic_probe(device_t dev)
{
@ -211,6 +224,9 @@ static device_method_t syscon_generic_dmethods[] = {
DEVMETHOD(device_attach, syscon_generic_attach),
DEVMETHOD(device_detach, syscon_generic_detach),
DEVMETHOD(syscon_device_lock, syscon_generic_lock),
DEVMETHOD(syscon_device_unlock, syscon_generic_unlock),
DEVMETHOD_END
};

View File

@ -35,6 +35,36 @@ HEADER {
int syscon_get_handle_default(device_t dev, struct syscon **syscon);
}
CODE {
#include <sys/systm.h>
#include <sys/bus.h>
int
syscon_get_handle_default(device_t dev, struct syscon **syscon)
{
device_t parent;
parent = device_get_parent(dev);
if (parent == NULL)
return (ENODEV);
return (SYSCON_GET_HANDLE(parent, syscon));
}
static void
syscon_device_lock_default(device_t dev)
{
panic("syscon_device_lock is not implemented");
};
static void
syscon_device_unlock_default(device_t dev)
{
panic("syscon_device_unlock is not implemented");
};
}
METHOD int init {
struct syscon *syscon;
};
@ -64,6 +94,38 @@ METHOD int modify_4 {
uint32_t set_bits;
};
/**
* Unlocked verion of access function
*/
METHOD uint32_t unlocked_read_4 {
struct syscon *syscon;
bus_size_t offset;
};
METHOD int unlocked_write_4 {
struct syscon *syscon;
bus_size_t offset;
uint32_t val;
};
METHOD int unlocked_modify_4 {
struct syscon *syscon;
bus_size_t offset;
uint32_t clear_bits;
uint32_t set_bits;
};
/**
* Locking for exclusive access to underlying device
*/
METHOD void device_lock {
device_t dev;
} DEFAULT syscon_device_lock_default;
METHOD void device_unlock {
device_t dev;
} DEFAULT syscon_device_unlock_default;
/**
* Get syscon handle from parent driver
*/