xref: /freebsd/sys/dev/uart/uart_dev_snps.c (revision 53b5393319dd7b54d63e8bf9aa82e48618552ecb)
1  /*-
2   * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
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   * 1. Redistributions of source code must retain the above copyright
8   *    notice, this list of conditions and the following disclaimer.
9   * 2. Redistributions in binary form must reproduce the above copyright
10   *    notice, this list of conditions and the following disclaimer in the
11   *    documentation and/or other materials provided with the distribution.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23   * SUCH DAMAGE.
24   */
25  
26  #include <sys/param.h>
27  #include <sys/systm.h>
28  #include <sys/bus.h>
29  #include <sys/kernel.h>
30  #include <sys/module.h>
31  #include <machine/bus.h>
32  
33  #include <dev/uart/uart.h>
34  #include <dev/uart/uart_bus.h>
35  #include <dev/uart/uart_cpu_fdt.h>
36  #include <dev/uart/uart_dev_ns8250.h>
37  
38  #include <dev/ofw/ofw_bus.h>
39  #include <dev/ofw/ofw_bus_subr.h>
40  
41  #include <dev/clk/clk.h>
42  #include <dev/hwreset/hwreset.h>
43  
44  #include "uart_if.h"
45  
46  struct snps_softc {
47  	struct ns8250_softc	ns8250;
48  
49  	clk_t			baudclk;
50  	clk_t			apb_pclk;
51  	hwreset_t		reset;
52  };
53  
54  /*
55   * To use early printf on 64 bits Allwinner SoC, add to kernel config
56   * options SOCDEV_PA=0x0
57   * options EARLY_PRINTF=snps
58   *
59   * To use early printf on 32 bits Allwinner SoC, add to kernel config
60   * options SOCDEV_PA=0x01C00000
61   * options SOCDEV_VA=0x10000000
62   * options EARLY_PRINTF=snps
63   *
64   * remove the if 0
65  */
66  #if CHECK_EARLY_PRINTF(snps)
67  static void
68  uart_snps_early_putc(int c)
69  {
70  	volatile uint32_t *stat;
71  	volatile uint32_t *tx;
72  
73  #ifdef ALLWINNER_64
74  	stat = (uint32_t *) (socdev_va + 0x1C2807C);
75  	tx = (uint32_t *) (socdev_va + 0x1C28000);
76  #endif
77  #ifdef ALLWINNER_32
78  	stat = (uint32_t *) (socdev_va + 0x2807C);
79  	tx = (uint32_t *) (socdev_va + 0x28000);
80  #endif
81  
82  	while ((*stat & (1 << 2)) == 0)
83  		continue;
84  	*tx = c;
85  }
86  early_putc_t *early_putc = uart_snps_early_putc;
87  #endif /* CHECK_EARLY_PRINTF */
88  
89  static kobj_method_t snps_methods[] = {
90  	KOBJMETHOD(uart_probe,		ns8250_bus_probe),
91  	KOBJMETHOD(uart_attach,		ns8250_bus_attach),
92  	KOBJMETHOD(uart_detach,		ns8250_bus_detach),
93  	KOBJMETHOD(uart_flush,		ns8250_bus_flush),
94  	KOBJMETHOD(uart_getsig,		ns8250_bus_getsig),
95  	KOBJMETHOD(uart_ioctl,		ns8250_bus_ioctl),
96  	KOBJMETHOD(uart_ipend,		ns8250_bus_ipend),
97  	KOBJMETHOD(uart_param,		ns8250_bus_param),
98  	KOBJMETHOD(uart_receive,	ns8250_bus_receive),
99  	KOBJMETHOD(uart_setsig,		ns8250_bus_setsig),
100  	KOBJMETHOD(uart_transmit,	ns8250_bus_transmit),
101  	KOBJMETHOD(uart_txbusy,		ns8250_bus_txbusy),
102  	KOBJMETHOD(uart_grab,		ns8250_bus_grab),
103  	KOBJMETHOD(uart_ungrab,		ns8250_bus_ungrab),
104  	KOBJMETHOD_END
105  };
106  
107  struct uart_class uart_snps_class = {
108  	"snps",
109  	snps_methods,
110  	sizeof(struct snps_softc),
111  	.uc_ops = &uart_ns8250_ops,
112  	.uc_range = 8,
113  	.uc_rclk = 0,
114  };
115  
116  static struct ofw_compat_data compat_data[] = {
117  	{ "snps,dw-apb-uart",		(uintptr_t)&uart_snps_class },
118  	{ "marvell,armada-38x-uart",	(uintptr_t)&uart_snps_class },
119  	{ NULL,				(uintptr_t)NULL }
120  };
121  UART_FDT_CLASS(compat_data);
122  
123  static int
124  snps_get_clocks(device_t dev, clk_t *baudclk, clk_t *apb_pclk)
125  {
126  
127  	*baudclk = NULL;
128  	*apb_pclk = NULL;
129  
130  	/* Baud clock is either named "baudclk", or there is a single
131  	 * unnamed clock.
132  	 */
133  	if (clk_get_by_ofw_name(dev, 0, "baudclk", baudclk) != 0 &&
134  	    clk_get_by_ofw_index(dev, 0, 0, baudclk) != 0)
135  		return (ENOENT);
136  
137  	/* APB peripheral clock is optional */
138  	(void)clk_get_by_ofw_name(dev, 0, "apb_pclk", apb_pclk);
139  
140  	return (0);
141  }
142  
143  static int
144  snps_probe(device_t dev)
145  {
146  	struct snps_softc *sc;
147  	struct uart_class *uart_class;
148  	phandle_t node;
149  	uint32_t shift, iowidth, clock;
150  	uint64_t freq;
151  	int error;
152  	clk_t baudclk, apb_pclk;
153  	hwreset_t reset;
154  
155  	if (!ofw_bus_status_okay(dev))
156  		return (ENXIO);
157  
158  	uart_class = (struct uart_class *)ofw_bus_search_compatible(dev,
159  	    compat_data)->ocd_data;
160  	if (uart_class == NULL)
161  		return (ENXIO);
162  
163  	freq = 0;
164  	sc = device_get_softc(dev);
165  	sc->ns8250.base.sc_class = uart_class;
166  
167  	node = ofw_bus_get_node(dev);
168  	if (OF_getencprop(node, "reg-shift", &shift, sizeof(shift)) <= 0)
169  		shift = 0;
170  	if (OF_getencprop(node, "reg-io-width", &iowidth, sizeof(iowidth)) <= 0)
171  		iowidth = 1;
172  	if (OF_getencprop(node, "clock-frequency", &clock, sizeof(clock)) <= 0)
173  		clock = 0;
174  
175  	if (hwreset_get_by_ofw_idx(dev, 0, 0, &reset) == 0) {
176  		error = hwreset_deassert(reset);
177  		if (error != 0) {
178  			device_printf(dev, "cannot de-assert reset\n");
179  			return (error);
180  		}
181  	}
182  
183  	if (snps_get_clocks(dev, &baudclk, &apb_pclk) == 0) {
184  		error = clk_enable(baudclk);
185  		if (error != 0) {
186  			device_printf(dev, "cannot enable baud clock\n");
187  			return (error);
188  		}
189  		if (apb_pclk != NULL) {
190  			error = clk_enable(apb_pclk);
191  			if (error != 0) {
192  				device_printf(dev,
193  				    "cannot enable peripheral clock\n");
194  				return (error);
195  			}
196  		}
197  
198  		if (clock == 0) {
199  			error = clk_get_freq(baudclk, &freq);
200  			if (error != 0) {
201  				device_printf(dev, "cannot get frequency\n");
202  				return (error);
203  			}
204  			clock = (uint32_t)freq;
205  		}
206  	}
207  
208  	if (bootverbose && clock == 0)
209  		device_printf(dev, "could not determine frequency\n");
210  
211  	error = uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, UART_F_BUSY_DETECT);
212  	if (error > 0)
213  		return (error);
214  
215  	/* XXX uart_bus_probe has changed the softc, so refresh it */
216  	sc = device_get_softc(dev);
217  
218  	/* Store clock and reset handles for detach */
219  	sc->baudclk = baudclk;
220  	sc->apb_pclk = apb_pclk;
221  	sc->reset = reset;
222  
223  	return (BUS_PROBE_VENDOR);
224  }
225  
226  static int
227  snps_attach(device_t dev)
228  {
229  	phandle_t node;
230  	int ret;
231  
232  	ret = uart_bus_attach(dev);
233  	if (ret == 0) {
234  		node = ofw_bus_get_node(dev);
235  		/* Set up phandle to dev mapping */
236  		OF_device_register_xref(OF_xref_from_node(node), dev);
237  	}
238  
239  	return (ret);
240  }
241  
242  static int
243  snps_detach(device_t dev)
244  {
245  	struct snps_softc *sc;
246  	clk_t baudclk, apb_pclk;
247  	hwreset_t reset;
248  	int error;
249  
250  	sc = device_get_softc(dev);
251  	baudclk = sc->baudclk;
252  	apb_pclk = sc->apb_pclk;
253  	reset = sc->reset;
254  
255  	error = uart_bus_detach(dev);
256  	if (error != 0)
257  		return (error);
258  
259  	if (reset != NULL) {
260  		error = hwreset_assert(reset);
261  		if (error != 0) {
262  			device_printf(dev, "cannot assert reset\n");
263  			return (error);
264  		}
265  		hwreset_release(reset);
266  	}
267  	if (apb_pclk != NULL) {
268  		error = clk_release(apb_pclk);
269  		if (error != 0) {
270  			device_printf(dev, "cannot release peripheral clock\n");
271  			return (error);
272  		}
273  	}
274  	if (baudclk != NULL) {
275  		error = clk_release(baudclk);
276  		if (error != 0) {
277  			device_printf(dev, "cannot release baud clock\n");
278  			return (error);
279  		}
280  	}
281  
282  	return (0);
283  }
284  
285  static device_method_t snps_bus_methods[] = {
286  	/* Device interface */
287  	DEVMETHOD(device_probe,		snps_probe),
288  	DEVMETHOD(device_attach,	snps_attach),
289  	DEVMETHOD(device_detach, 	snps_detach),
290  	DEVMETHOD_END
291  };
292  
293  static driver_t snps_uart_driver = {
294  	uart_driver_name,
295  	snps_bus_methods,
296  	sizeof(struct snps_softc)
297  };
298  
299  DRIVER_MODULE(uart_snps, simplebus, snps_uart_driver, 0, 0);
300