1/*-
2 * Copyright (c) 2012 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed by SRI International and the University of
6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7 * ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/bus.h>
36#include <sys/conf.h>
37#include <sys/consio.h>				/* struct vt_mode */
38#include <sys/endian.h>
39#include <sys/fbio.h>				/* video_adapter_t */
40#include <sys/lock.h>
41#include <sys/mutex.h>
42#include <sys/rman.h>
43#include <sys/systm.h>
44#include <sys/uio.h>
45
46#include <machine/bus.h>
47#include <machine/resource.h>
48#include <machine/vm.h>
49
50#include <dev/terasic/mtl/terasic_mtl.h>
51
52static d_mmap_t terasic_mtl_text_mmap;
53static d_read_t terasic_mtl_text_read;
54static d_write_t terasic_mtl_text_write;
55
56static struct cdevsw terasic_mtl_text_cdevsw = {
57	.d_version =	D_VERSION,
58	.d_mmap =	terasic_mtl_text_mmap,
59	.d_read =	terasic_mtl_text_read,
60	.d_write =	terasic_mtl_text_write,
61	.d_name =	"terasic_mtl_text",
62};
63
64/*
65 * All I/O to/from the mtl device must be 16-bit, and aligned to 16-bit.
66 */
67static int
68terasic_mtl_text_read(struct cdev *dev, struct uio *uio, int flag)
69{
70	struct terasic_mtl_softc *sc;
71	u_long offset, size;
72	uint16_t v;
73	int error;
74
75	if (uio->uio_offset < 0 || uio->uio_offset % 2 != 0 ||
76	    uio->uio_resid % 2 != 0)
77		return (ENODEV);
78	sc = dev->si_drv1;
79	size = rman_get_size(sc->mtl_text_res);
80	error = 0;
81	if ((uio->uio_offset + uio->uio_resid < 0) ||
82	    (uio->uio_offset + uio->uio_resid > size))
83		return (ENODEV);
84	while (uio->uio_resid > 0) {
85		offset = uio->uio_offset;
86		if (offset + sizeof(v) > size)
87			return (ENODEV);
88		v = bus_read_2(sc->mtl_text_res, offset);
89		error = uiomove(&v, sizeof(v), uio);
90		if (error)
91			return (error);
92	}
93	return (error);
94}
95
96static int
97terasic_mtl_text_write(struct cdev *dev, struct uio *uio, int flag)
98{
99	struct terasic_mtl_softc *sc;
100	u_long offset, size;
101	uint16_t v;
102	int error;
103
104	if (uio->uio_offset < 0 || uio->uio_offset % 2 != 0 ||
105	    uio->uio_resid % 2 != 0)
106		return (ENODEV);
107	sc = dev->si_drv1;
108	size = rman_get_size(sc->mtl_text_res);
109	error = 0;
110	while (uio->uio_resid > 0) {
111		offset = uio->uio_offset;
112		if (offset + sizeof(v) > size)
113			return (ENODEV);
114		error = uiomove(&v, sizeof(v), uio);
115		if (error)
116			return (error);
117		bus_write_2(sc->mtl_text_res, offset, v);
118	}
119	return (error);
120}
121
122static int
123terasic_mtl_text_mmap(struct cdev *dev, vm_ooffset_t offset,
124    vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
125{
126	struct terasic_mtl_softc *sc;
127	int error;
128
129	sc = dev->si_drv1;
130	error = 0;
131	if (trunc_page(offset) == offset &&
132	    rman_get_size(sc->mtl_text_res) >= offset + PAGE_SIZE) {
133		*paddr = rman_get_start(sc->mtl_text_res) + offset;
134		*memattr = VM_MEMATTR_UNCACHEABLE;
135	} else
136		error = ENODEV;
137	return (error);
138}
139
140void
141terasic_mtl_text_putc(struct terasic_mtl_softc *sc, u_int x, u_int y,
142    uint8_t c, uint8_t a)
143{
144	u_int offset;
145	uint16_t v;
146
147	KASSERT(x < TERASIC_MTL_COLS, ("%s: TERASIC_MTL_COLS", __func__));
148	KASSERT(y < TERASIC_MTL_ROWS, ("%s: TERASIC_MTL_ROWS", __func__));
149
150	offset = sizeof(uint16_t) * (x + y * TERASIC_MTL_COLS);
151	v = (c << TERASIC_MTL_TEXTFRAMEBUF_CHAR_SHIFT) |
152	    (a << TERASIC_MTL_TEXTFRAMEBUF_ATTR_SHIFT);
153	v = htole16(v);
154	bus_write_2(sc->mtl_text_res, offset, v);
155}
156
157int
158terasic_mtl_text_attach(struct terasic_mtl_softc *sc)
159{
160	uint32_t v;
161
162	terasic_mtl_reg_textframebufaddr_get(sc, &v);
163	if (v != TERASIC_MTL_TEXTFRAMEBUF_EXPECTED_ADDR) {
164		device_printf(sc->mtl_dev, "%s: unexpected text frame buffer "
165		    "address (%08x); cannot attach\n", __func__, v);
166		return (ENXIO);
167	}
168
169	sc->mtl_text_cdev = make_dev(&terasic_mtl_text_cdevsw, sc->mtl_unit,
170	    UID_ROOT, GID_WHEEL, 0400, "mtl_text%d", sc->mtl_unit);
171	if (sc->mtl_text_cdev == NULL) {
172		device_printf(sc->mtl_dev, "%s: make_dev failed\n", __func__);
173		return (ENXIO);
174	}
175	/* XXXRW: Slight race between make_dev(9) and here. */
176	TERASIC_MTL_LOCK_INIT(sc);
177	sc->mtl_text_cdev->si_drv1 = sc;
178	return (0);
179}
180
181void
182terasic_mtl_text_detach(struct terasic_mtl_softc *sc)
183{
184
185	if (sc->mtl_text_cdev != NULL) {
186		destroy_dev(sc->mtl_text_cdev);
187		TERASIC_MTL_LOCK_DESTROY(sc);
188	}
189}
190