Date: Fri, 16 Jan 2015 10:09:59 -0800 From: Thomas Skibo <thomasskibo@sbcglobal.net> To: =?utf-8?Q?Manuel_St=C3=BChn?= <freebsdnewbie@freenet.de> Cc: freebsd-arm@freebsd.org Subject: Re: mmap-issue Message-ID: <A46186E9-BAFD-47F1-AC23-2EE5FEDECDCB@sbcglobal.net> In-Reply-To: <54B945FB.10609@freenet.de>
index | next in thread | previous in thread | raw e-mail
[-- Attachment #1 --]
> On Jan 16, 2015, at 9:10 AM, Manuel Stühn <freebsdnewbie@freenet.de> wrote:
>
>
> I mapped for testing purposes the AM335x-GPIO-registers from /dev/mem.
> Writing into these mmap'ed registers for toggling some LEDs does not immediatly take effect. I have to call it several times to get one LED-toggle. Is there any data caching I'm missing?
>
> Thanks for hints.
>
Yes. On arm, /dev/mem always maps pages into user space with the data cache enabled. I’ve run into this too.
To work around it, I ported some code from another architecture (I can’t remember which) into sys/arm/arm/mem.c to allow memcontrol(8) to declare some physical addresses as uncacheable. I don’t know if this is a proper long-term solution.
This patch has been lying around on my computer for a while and so I haven’t tested it in a while.
-------
Thomas Skibo
thomasskibo@sbcglobal.net
[-- Attachment #2 --]
Index: sys/arm/arm/mem.c
===================================================================
--- sys/arm/arm/mem.c (revision 269763)
+++ sys/arm/arm/mem.c (working copy)
@@ -68,9 +68,21 @@
/*
* Used in /dev/mem drivers and elsewhere
*/
+static void arm_mrinit(struct mem_range_softc *);
+static int arm_mrset(struct mem_range_softc *, struct mem_range_desc *, int *);
+
MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
-struct mem_range_softc mem_range_softc;
+static struct mem_range_ops arm_mem_range_ops = {
+ arm_mrinit,
+ arm_mrset,
+ NULL,
+ NULL
+};
+struct mem_range_softc mem_range_softc = {
+ &arm_mem_range_ops,
+ 0, 0, NULL
+};
/* ARGSUSED */
int
@@ -152,12 +164,132 @@
int
memmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
- int prot __unused, vm_memattr_t *memattr __unused)
+ int prot __unused, vm_memattr_t *memattr)
{
- if (dev2unit(dev) == CDEV_MINOR_MEM)
+ int i;
+
+ if (dev2unit(dev) == CDEV_MINOR_KMEM)
+ *paddr = vtophys(offset);
+ else if (dev2unit(dev) == CDEV_MINOR_MEM) {
*paddr = offset;
- else if (dev2unit(dev) == CDEV_MINOR_KMEM)
- *paddr = vtophys(offset);
+
+ for (i = 0; i < mem_range_softc.mr_ndesc; i++) {
+ if ((mem_range_softc.mr_desc[i].mr_flags &
+ MDF_ACTIVE) != 0 &&
+ offset >= mem_range_softc.mr_desc[i].mr_base &&
+ offset < mem_range_softc.mr_desc[i].mr_base +
+ mem_range_softc.mr_desc[i].mr_len &&
+ (mem_range_softc.mr_desc[i].mr_flags &
+ MDF_ATTRMASK) == MDF_UNCACHEABLE) {
+ *memattr = VM_MEMATTR_UNCACHEABLE;
+ break;
+ }
+ }
+ }
/* else panic! */
return (0);
}
+
+static void
+arm_mrinit(struct mem_range_softc *sc)
+{
+ sc->mr_cap = 0;
+ sc->mr_ndesc = 8; /* XXX: Should be dynamically expandable */
+ sc->mr_desc = malloc(sc->mr_ndesc * sizeof(struct mem_range_desc),
+ M_MEMDESC, M_NOWAIT | M_ZERO);
+ if (sc->mr_desc == NULL)
+ panic("%s: malloc returns NULL", __func__);
+}
+
+static int
+arm_mrset(struct mem_range_softc *sc, struct mem_range_desc *desc, int *arg)
+{
+ int i;
+
+ switch(*arg) {
+ case MEMRANGE_SET_UPDATE:
+ for (i = 0; i < sc->mr_ndesc; i++) {
+ if (!sc->mr_desc[i].mr_len) {
+ sc->mr_desc[i] = *desc;
+ sc->mr_desc[i].mr_flags |= MDF_ACTIVE;
+ return (0);
+ }
+ if (sc->mr_desc[i].mr_base == desc->mr_base &&
+ sc->mr_desc[i].mr_len == desc->mr_len)
+ return (EEXIST);
+ }
+ return (ENOSPC);
+ case MEMRANGE_SET_REMOVE:
+ for (i = 0; i < sc->mr_ndesc; i++)
+ if (sc->mr_desc[i].mr_base == desc->mr_base &&
+ sc->mr_desc[i].mr_len == desc->mr_len) {
+ bzero(&sc->mr_desc[i], sizeof(sc->mr_desc[i]));
+ return (0);
+ }
+ return (ENOENT);
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ return (0);
+}
+
+/*
+ * Operations for changing memory attributes.
+ *
+ * This is basically just an ioctl shim for mem_range_attr_get
+ * and mem_range_attr_set.
+ */
+/* ARGSUSED */
+int
+memioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags,
+ struct thread *td)
+{
+ int nd, error = 0;
+ struct mem_range_op *mo = (struct mem_range_op *)data;
+ struct mem_range_desc *md;
+
+ /* is this for us? */
+ if ((cmd != MEMRANGE_GET) &&
+ (cmd != MEMRANGE_SET))
+ return (ENOTTY);
+
+ /* any chance we can handle this? */
+ if (mem_range_softc.mr_op == NULL)
+ return (EOPNOTSUPP);
+
+ /* do we have any descriptors? */
+ if (mem_range_softc.mr_ndesc == 0)
+ return (ENXIO);
+
+ switch (cmd) {
+ case MEMRANGE_GET:
+ nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
+ if (nd > 0) {
+ md = (struct mem_range_desc *)
+ malloc(nd * sizeof(struct mem_range_desc),
+ M_MEMDESC, M_WAITOK);
+ error = mem_range_attr_get(md, &nd);
+ if (!error)
+ error = copyout(md, mo->mo_desc,
+ nd * sizeof(struct mem_range_desc));
+ free(md, M_MEMDESC);
+ }
+ else
+ nd = mem_range_softc.mr_ndesc;
+ mo->mo_arg[0] = nd;
+ break;
+
+ case MEMRANGE_SET:
+ md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc),
+ M_MEMDESC, M_WAITOK);
+ error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
+ /* clamp description string */
+ md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
+ if (error == 0)
+ error = mem_range_attr_set(md, &mo->mo_arg[0]);
+ free(md, M_MEMDESC);
+ break;
+ }
+ return (error);
+}
Index: sys/arm/include/memdev.h
===================================================================
--- sys/arm/include/memdev.h (revision 269763)
+++ sys/arm/include/memdev.h (working copy)
@@ -34,7 +34,7 @@
d_open_t memopen;
d_read_t memrw;
+d_ioctl_t memioctl;
d_mmap_t memmmap;
-#define memioctl (d_ioctl_t *)NULL
#endif /* _MACHINE_MEMDEV_H_ */
help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?A46186E9-BAFD-47F1-AC23-2EE5FEDECDCB>
