sc16is7xxspi.c revision 1.1 1 1.1 brad /* $NetBSD: sc16is7xxspi.c,v 1.1 2025/10/24 23:16:11 brad Exp $ */
2 1.1 brad
3 1.1 brad /*
4 1.1 brad * Copyright (c) 2025 Brad Spencer <brad (at) anduin.eldar.org>
5 1.1 brad *
6 1.1 brad * Permission to use, copy, modify, and distribute this software for any
7 1.1 brad * purpose with or without fee is hereby granted, provided that the above
8 1.1 brad * copyright notice and this permission notice appear in all copies.
9 1.1 brad *
10 1.1 brad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 brad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 brad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 brad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 brad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 brad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 brad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 brad */
18 1.1 brad
19 1.1 brad #include <sys/cdefs.h>
20 1.1 brad __KERNEL_RCSID(0, "$NetBSD: sc16is7xxspi.c,v 1.1 2025/10/24 23:16:11 brad Exp $");
21 1.1 brad
22 1.1 brad /*
23 1.1 brad * SPI frontend driver for the SC16IS7xx UART bridge.
24 1.1 brad * The heavy lifting is done by the general sc16is7xx(4)
25 1.1 brad * driver and the com(4) backend.
26 1.1 brad */
27 1.1 brad
28 1.1 brad #include <sys/param.h>
29 1.1 brad #include <sys/systm.h>
30 1.1 brad #include <sys/kernel.h>
31 1.1 brad #include <sys/device.h>
32 1.1 brad #include <sys/module.h>
33 1.1 brad #include <sys/conf.h>
34 1.1 brad #include <sys/sysctl.h>
35 1.1 brad #include <sys/mutex.h>
36 1.1 brad #include <sys/condvar.h>
37 1.1 brad #include <sys/pool.h>
38 1.1 brad #include <sys/kmem.h>
39 1.1 brad
40 1.1 brad #include <dev/i2c/i2cvar.h>
41 1.1 brad #include <dev/spi/spivar.h>
42 1.1 brad #include <dev/ic/sc16is7xxreg.h>
43 1.1 brad #include <dev/ic/sc16is7xxvar.h>
44 1.1 brad
45 1.1 brad #include "opt_sc16is7xx.h"
46 1.1 brad
47 1.1 brad struct sc16is7xx_spi_softc {
48 1.1 brad struct sc16is7xx_sc sc_sc16is7xx;
49 1.1 brad spi_handle_t sc_sh;
50 1.1 brad };
51 1.1 brad
52 1.1 brad #define SC16IS7XX_TO_SPI(sc) \
53 1.1 brad container_of((sc), struct sc16is7xx_spi_softc, sc_sc16is7xx)
54 1.1 brad
55 1.1 brad static int sc16is7xxspi_match(device_t, cfdata_t, void *);
56 1.1 brad static void sc16is7xxspi_attach(device_t, device_t, void *);
57 1.1 brad static int sc16is7xxspi_detach(device_t, int);
58 1.1 brad
59 1.1 brad CFATTACH_DECL_NEW(sc16is7xxspi, sizeof(struct sc16is7xx_sc),
60 1.1 brad sc16is7xxspi_match, sc16is7xxspi_attach, sc16is7xxspi_detach, NULL);
61 1.1 brad
62 1.1 brad static int
63 1.1 brad sc16is7xxspi_read_register_direct(spi_handle_t sh,
64 1.1 brad uint8_t reg, int channel, uint8_t *buf, size_t blen)
65 1.1 brad {
66 1.1 brad int error;
67 1.1 brad uint8_t xreg;
68 1.1 brad
69 1.1 brad xreg = ((reg << 3) | (channel << 1)) | 0x80;
70 1.1 brad
71 1.1 brad error = spi_send_recv(sh, 1, &xreg, blen, buf);
72 1.1 brad return error;
73 1.1 brad }
74 1.1 brad
75 1.1 brad static int
76 1.1 brad sc16is7xxspi_write_register_direct(spi_handle_t sh,
77 1.1 brad uint8_t reg, int channel, uint8_t *buf, size_t blen)
78 1.1 brad {
79 1.1 brad int error;
80 1.1 brad uint8_t xreg;
81 1.1 brad struct iovec iov[2];
82 1.1 brad
83 1.1 brad xreg = (reg << 3) | (channel << 1);
84 1.1 brad
85 1.1 brad KASSERTMSG(!(xreg & 0x80), "xreg=%02x", xreg); /* panic if this ends up
86 1.1 brad * trying to be a read */
87 1.1 brad
88 1.1 brad iov[0].iov_len = 1;
89 1.1 brad iov[0].iov_base = &xreg;
90 1.1 brad iov[1].iov_len = blen;
91 1.1 brad iov[1].iov_base = buf;
92 1.1 brad error = spi_sendv(sh, &iov[0], 2);
93 1.1 brad
94 1.1 brad return error;
95 1.1 brad }
96 1.1 brad /* Use these after the hand off to the general driver happens */
97 1.1 brad
98 1.1 brad static int
99 1.1 brad sc16is7xxspi_read_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel,
100 1.1 brad uint8_t *buf, size_t blen)
101 1.1 brad {
102 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc);
103 1.1 brad int error;
104 1.1 brad
105 1.1 brad KASSERT(blen > 0);
106 1.1 brad
107 1.1 brad error = sc16is7xxspi_read_register_direct(ssc->sc_sh,
108 1.1 brad reg, channel, buf, blen);
109 1.1 brad
110 1.1 brad return error;
111 1.1 brad }
112 1.1 brad
113 1.1 brad static int
114 1.1 brad sc16is7xxspi_write_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel,
115 1.1 brad uint8_t *buf, size_t blen)
116 1.1 brad {
117 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc);
118 1.1 brad int error;
119 1.1 brad
120 1.1 brad KASSERT(blen > 0);
121 1.1 brad
122 1.1 brad error = sc16is7xxspi_write_register_direct(ssc->sc_sh,
123 1.1 brad reg, channel, buf, blen);
124 1.1 brad
125 1.1 brad return error;
126 1.1 brad }
127 1.1 brad
128 1.1 brad static void
129 1.1 brad sc16is7xxspi_copy_handles(struct sc16is7xx_sc *sc, struct com_regs *regs)
130 1.1 brad {
131 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc);
132 1.1 brad
133 1.1 brad regs->cr_sh = ssc->sc_sh;
134 1.1 brad }
135 1.1 brad
136 1.1 brad static const struct sc16is7xx_accessfuncs sc16is7xx_spi_accessfuncs = {
137 1.1 brad .read_reg = sc16is7xxspi_read_register,
138 1.1 brad .write_reg = sc16is7xxspi_write_register,
139 1.1 brad .copy_handles = sc16is7xxspi_copy_handles,
140 1.1 brad };
141 1.1 brad /* These will be used by dev/ic/com.c, conform to what is expected */
142 1.1 brad
143 1.1 brad static uint8_t
144 1.1 brad sc16is7xx_spi_com_read_1(struct com_regs *regs, u_int reg)
145 1.1 brad {
146 1.1 brad uint8_t buf;
147 1.1 brad int error;
148 1.1 brad
149 1.1 brad error = sc16is7xxspi_read_register_direct(regs->cr_sh,
150 1.1 brad reg, regs->cr_channel, &buf, 1);
151 1.1 brad
152 1.1 brad if (!error)
153 1.1 brad return buf;
154 1.1 brad
155 1.1 brad return 0;
156 1.1 brad }
157 1.1 brad
158 1.1 brad static void
159 1.1 brad sc16is7xx_spi_com_write_1(struct com_regs *regs, u_int reg, uint8_t val)
160 1.1 brad {
161 1.1 brad sc16is7xxspi_write_register_direct(regs->cr_sh,
162 1.1 brad reg, regs->cr_channel, &val, 1);
163 1.1 brad }
164 1.1 brad
165 1.1 brad static void
166 1.1 brad sc16is7xx_spi_com_write_multi_1(struct com_regs *regs, u_int reg, const uint8_t *datap,
167 1.1 brad bus_size_t count)
168 1.1 brad {
169 1.1 brad sc16is7xxspi_write_register_direct(regs->cr_sh,
170 1.1 brad reg, regs->cr_channel, __UNCONST(datap), count);
171 1.1 brad }
172 1.1 brad
173 1.1 brad static const struct sc16is7xx_accessfuncs sc16is7xx_spi_com_accessfuncs = {
174 1.1 brad .com_read_1 = sc16is7xx_spi_com_read_1,
175 1.1 brad .com_write_1 = sc16is7xx_spi_com_write_1,
176 1.1 brad .com_write_multi_1 = sc16is7xx_spi_com_write_multi_1,
177 1.1 brad };
178 1.1 brad
179 1.1 brad static int
180 1.1 brad sc16is7xxspi_match(device_t parent, cfdata_t match, void *aux)
181 1.1 brad {
182 1.1 brad struct spi_attach_args *sa = aux;
183 1.1 brad int match_result;
184 1.1 brad
185 1.1 brad if (spi_use_direct_match(sa, sc16is7xx_compat_data, &match_result)) {
186 1.1 brad return match_result;
187 1.1 brad }
188 1.1 brad return SPI_MATCH_DEFAULT;
189 1.1 brad }
190 1.1 brad
191 1.1 brad #ifndef SC16IS7XX_SPI_FREQUENCY
192 1.1 brad #define SC16IS7XX_SPI_FREQUENCY 1
193 1.1 brad #endif
194 1.1 brad
195 1.1 brad static void
196 1.1 brad sc16is7xxspi_attach(device_t parent, device_t self, void *aux)
197 1.1 brad {
198 1.1 brad struct sc16is7xx_spi_softc *ssc = device_private(self);
199 1.1 brad struct sc16is7xx_sc *sc = &ssc->sc_sc16is7xx;
200 1.1 brad struct spi_attach_args *sa = aux;
201 1.1 brad int error;
202 1.1 brad
203 1.1 brad sc->sc_dev = self;
204 1.1 brad sc->sc_funcs = &sc16is7xx_spi_accessfuncs;
205 1.1 brad sc->sc_com_funcs = &sc16is7xx_spi_com_accessfuncs;
206 1.1 brad
207 1.1 brad ssc->sc_sh = sa->sa_handle;
208 1.1 brad
209 1.1 brad aprint_normal("\n");
210 1.1 brad aprint_normal_dev(sc->sc_dev, "SPI frequency %dMhz", SC16IS7XX_SPI_FREQUENCY);
211 1.1 brad
212 1.1 brad /* Configure for SPI mode 0 according to the data sheet. The chip will
213 1.1 brad * do up to 4Mhz or 15Mhz depending on the varient and does support
214 1.1 brad * other modes. */
215 1.1 brad error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(SC16IS7XX_SPI_FREQUENCY));
216 1.1 brad if (error) {
217 1.1 brad return;
218 1.1 brad }
219 1.1 brad sc16is7xx_attach(sc);
220 1.1 brad }
221 1.1 brad
222 1.1 brad static int
223 1.1 brad sc16is7xxspi_detach(device_t self, int flags)
224 1.1 brad {
225 1.1 brad struct sc16is7xx_spi_softc *ssc = device_private(self);
226 1.1 brad
227 1.1 brad return sc16is7xx_detach(&ssc->sc_sc16is7xx, flags);
228 1.1 brad }
229