Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/amigaone/cpld_x5000.c
39507 views
1
/*-
2
* Copyright (c) 2020 Justin Hibbits
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
*
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
*/
25
26
#include <sys/param.h>
27
#include <sys/systm.h>
28
#include <sys/conf.h>
29
#include <sys/kernel.h>
30
#include <sys/bus.h>
31
#include <sys/limits.h>
32
#include <sys/module.h>
33
#include <sys/malloc.h>
34
#include <sys/mutex.h>
35
#include <sys/rman.h>
36
#include <sys/sysctl.h>
37
38
#include <machine/bus.h>
39
40
#include <dev/ofw/ofw_bus.h>
41
#include <dev/ofw/ofw_bus_subr.h>
42
43
#include "cpld.h"
44
45
/*
46
* A driver for the AmigaOne X5000 "Cyrus+" CPLD.
47
*
48
* This is the interface between the CPU and the "Xena" (XMOS) chip. Since the
49
* XMOS is programmable via a SPI-attached flash memory, there's no direct
50
* driver written for the Xena attachment. Instead, a userspace process would
51
* communicate with the Xena by issuing ioctl()s to this CPLD.
52
*/
53
54
/* Resource access addresses. */
55
#define CPLD_MEM_ADDR 0x0000
56
#define CPLD_MEM_DATA 0x8000
57
58
#define CPLD_MAX_DRAM_WORDS 0x800
59
60
/* CPLD Registers. */
61
#define CPLD_REG_SIG1 0x00
62
#define CPLD_REG_SIG2 0x01
63
#define CPLD_REG_HWREV 0x02
64
#define CPLD_REG_MBC2X 0x05
65
#define CPLD_REG_MBX2C 0x06
66
#define CPLD_REG_XDEBUG 0x0c
67
#define CPLD_REG_XJTAG 0x0d
68
#define CPLD_REG_FAN_TACHO 0x10
69
#define CPLD_REG_DATE_LW 0x21
70
#define CPLD_REG_DATE_UW 0x22
71
#define CPLD_REG_TIME_LW 0x23
72
#define CPLD_REG_TIME_UW 0x24
73
#define CPLD_REG_SCR1 0x30
74
#define CPLD_REG_SCR2 0x31
75
#define CPLD_REG_RAM 0x8000
76
77
struct cpld_softc {
78
device_t sc_dev;
79
struct resource *sc_mem;
80
struct cdev *sc_cdev;
81
struct mtx sc_mutex;
82
bool sc_isopen;
83
};
84
85
static d_open_t cpld_open;
86
static d_close_t cpld_close;
87
static d_ioctl_t cpld_ioctl;
88
89
static struct cdevsw cpld_cdevsw = {
90
.d_version = D_VERSION,
91
.d_open = cpld_open,
92
.d_close = cpld_close,
93
.d_ioctl = cpld_ioctl,
94
.d_name = "nvram",
95
};
96
97
static device_probe_t cpld_probe;
98
static device_attach_t cpld_attach;
99
static int cpld_fan_sysctl(SYSCTL_HANDLER_ARGS);
100
101
static device_method_t cpld_methods[] = {
102
DEVMETHOD(device_probe, cpld_probe),
103
DEVMETHOD(device_attach, cpld_attach),
104
105
DEVMETHOD_END
106
};
107
108
static driver_t cpld_driver = {
109
"cpld",
110
cpld_methods,
111
sizeof(struct cpld_softc)
112
};
113
114
DRIVER_MODULE(cpld, lbc, cpld_driver, 0, 0);
115
116
static void
117
cpld_write(struct cpld_softc *sc, int addr, int data)
118
{
119
bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr);
120
bus_write_2(sc->sc_mem, CPLD_MEM_DATA, data);
121
}
122
123
static int
124
cpld_read(struct cpld_softc *sc, int addr)
125
{
126
bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr);
127
128
return (bus_read_2(sc->sc_mem, CPLD_MEM_DATA));
129
}
130
131
static int
132
cpld_probe(device_t dev)
133
{
134
if (!ofw_bus_is_compatible(dev, "aeon,cyrus-cpld"))
135
return (ENXIO);
136
137
device_set_desc(dev, "AmigaOne Cyrus CPLD");
138
139
return (BUS_PROBE_GENERIC);
140
}
141
142
static int
143
cpld_attach(device_t dev)
144
{
145
struct make_dev_args mda;
146
struct cpld_softc *sc;
147
int rid;
148
int date, time, tmp;
149
int err;
150
struct sysctl_ctx_list *ctx;
151
struct sysctl_oid *tree;
152
153
sc = device_get_softc(dev);
154
sc->sc_dev = dev;
155
156
rid = 0;
157
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
158
RF_ACTIVE|RF_SHAREABLE);
159
if (sc->sc_mem == NULL) {
160
device_printf(dev, "Unable to allocate memory resource.\n");
161
return (ENXIO);
162
}
163
mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF);
164
if (bootverbose) {
165
date = (cpld_read(sc, CPLD_REG_DATE_UW) << 16) |
166
cpld_read(sc, CPLD_REG_DATE_LW);
167
time = (cpld_read(sc, CPLD_REG_TIME_UW) << 16) |
168
cpld_read(sc, CPLD_REG_TIME_LW);
169
170
device_printf(dev, "Build date: %04x-%02x-%02x\n",
171
(date >> 16) & 0xffff, (date >> 8) & 0xff, date & 0xff);
172
device_printf(dev, "Build time: %02x:%02x:%02x\n",
173
(time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff);
174
}
175
176
tmp = cpld_read(sc, CPLD_REG_HWREV);
177
device_printf(dev, "Hardware revision: %d\n", tmp);
178
179
ctx = device_get_sysctl_ctx(dev);
180
tree = device_get_sysctl_tree(dev);
181
182
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
183
"cpu_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
184
cpld_fan_sysctl, "I", "CPU Fan speed in RPM");
185
186
make_dev_args_init(&mda);
187
mda.mda_flags = MAKEDEV_CHECKNAME;
188
mda.mda_devsw = &cpld_cdevsw;
189
mda.mda_uid = UID_ROOT;
190
mda.mda_gid = GID_WHEEL;
191
mda.mda_mode = 0660;
192
mda.mda_si_drv1 = sc;
193
err = make_dev_s(&mda, &sc->sc_cdev, "cpld");
194
if (err != 0) {
195
device_printf(dev, "Error creating character device: %d\n", err);
196
device_printf(dev, "Only sysctl interfaces will be available.\n");
197
}
198
199
return (0);
200
}
201
202
static int
203
cpld_fan_sysctl(SYSCTL_HANDLER_ARGS)
204
{
205
struct cpld_softc *sc;
206
int error, old, rpm;
207
208
sc = arg1;
209
mtx_lock(&sc->sc_mutex);
210
/* Read until we get some level of read stability. */
211
rpm = cpld_read(sc, CPLD_REG_FAN_TACHO);
212
do {
213
old = rpm;
214
rpm = cpld_read(sc, CPLD_REG_FAN_TACHO);
215
} while (abs(rpm - old) > 10);
216
mtx_unlock(&sc->sc_mutex);
217
218
/* Convert RPS->RPM. */
219
rpm *= 60;
220
error = sysctl_handle_int(oidp, &rpm, 0, req);
221
222
return (error);
223
}
224
225
static int
226
cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td)
227
{
228
struct cpld_softc *sc = dev->si_drv1;
229
230
if (sc->sc_isopen)
231
return (EBUSY);
232
sc->sc_isopen = 1;
233
return (0);
234
}
235
236
static int
237
cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
238
{
239
struct cpld_softc *sc = dev->si_drv1;
240
241
sc->sc_isopen = 0;
242
return (0);
243
}
244
245
static int
246
cpld_send(device_t dev, struct cpld_cmd_data *d)
247
{
248
struct cpld_softc *sc;
249
uint16_t *word;
250
int i;
251
252
if (d->cmd > USHRT_MAX)
253
return (EINVAL);
254
255
sc = device_get_softc(dev);
256
257
mtx_lock(&sc->sc_mutex);
258
for (i = 0, word = d->words; i < d->len; i++, word++) {
259
if (i == 0)
260
cpld_write(sc, CPLD_REG_RAM, *word);
261
else
262
bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word);
263
}
264
265
cpld_write(sc, CPLD_REG_MBC2X, d->cmd);
266
mtx_unlock(&sc->sc_mutex);
267
268
return (0);
269
}
270
271
static int
272
cpld_recv(device_t dev, struct cpld_cmd_data *d)
273
{
274
struct cpld_softc *sc;
275
uint16_t *word;
276
int i;
277
278
sc = device_get_softc(dev);
279
280
mtx_lock(&sc->sc_mutex);
281
d->cmd = cpld_read(sc, CPLD_REG_MBX2C);
282
283
for (i = 0, word = d->words; i < d->len; i++, word++) {
284
if (i == 0)
285
*word = cpld_read(sc, CPLD_REG_RAM);
286
else
287
*word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA);
288
}
289
mtx_unlock(&sc->sc_mutex);
290
291
return (0);
292
}
293
294
static int
295
cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
296
{
297
struct cpld_softc *sc;
298
struct cpld_cmd_data *d;
299
void *xfer_data, *tmp;
300
int err;
301
302
sc = dev->si_drv1;
303
304
err = 0;
305
d = (struct cpld_cmd_data *)data;
306
if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) {
307
return (EINVAL);
308
}
309
xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK);
310
311
switch (cmd) {
312
case IOCCPLDSEND:
313
err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t));
314
d->words = xfer_data;
315
if (err == 0)
316
err = cpld_send(sc->sc_dev, d);
317
break;
318
case IOCCPLDRECV:
319
tmp = d->words;
320
d->words = xfer_data;
321
err = cpld_recv(sc->sc_dev, d);
322
d->words = tmp;
323
if (err == 0)
324
err = copyout(xfer_data, d->words,
325
d->len * sizeof(uint16_t));
326
break;
327
default:
328
err = ENOTTY;
329
break;
330
}
331
free(xfer_data, M_TEMP);
332
333
return (err);
334
}
335
336