1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.Org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/*
29 * Rockchip USB2PHY
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38#include <sys/rman.h>
39#include <sys/kernel.h>
40#include <sys/module.h>
41#include <sys/gpio.h>
42#include <machine/bus.h>
43
44#include <dev/fdt/fdt_common.h>
45#include <dev/ofw/ofw_bus.h>
46#include <dev/ofw/ofw_bus_subr.h>
47#include <dev/ofw/ofw_subr.h>
48
49#include <dev/extres/clk/clk.h>
50#include <dev/extres/phy/phy_usb.h>
51#include <dev/extres/regulator/regulator.h>
52#include <dev/extres/syscon/syscon.h>
53
54#include "clkdev_if.h"
55#include "syscon_if.h"
56
57#define	RK3399_GRF_USB20_PHY0_CON0	0x0
58#define	RK3399_GRF_USB20_PHY0_CON1	0x4
59#define	RK3399_GRF_USB20_PHY0_CON2	0x8
60#define	RK3399_GRF_USB20_PHY0_CON3	0xC
61
62struct rk_usb2phy_reg {
63	uint32_t	offset;
64	uint32_t	enable_mask;
65	uint32_t	disable_mask;
66};
67
68struct rk_usb2phy_regs {
69	struct rk_usb2phy_reg	clk_ctl;
70};
71
72struct rk_usb2phy_regs rk3399_regs = {
73	.clk_ctl = {
74		/* bit 4 put pll in suspend */
75		.enable_mask = 0x100000,
76		.disable_mask = 0x100010,
77	}
78};
79
80static struct ofw_compat_data compat_data[] = {
81	{ "rockchip,rk3399-usb2phy",	(uintptr_t)&rk3399_regs },
82	{ NULL,				0 }
83};
84
85struct rk_usb2phy_softc {
86	device_t		dev;
87	struct syscon		*grf;
88	regulator_t		phy_supply;
89	clk_t			clk;
90	int			mode;
91};
92
93/* Phy class and methods. */
94static int rk_usb2phy_enable(struct phynode *phynode, bool enable);
95static int rk_usb2phy_get_mode(struct phynode *phy, int *mode);
96static int rk_usb2phy_set_mode(struct phynode *phy, int mode);
97static phynode_method_t rk_usb2phy_phynode_methods[] = {
98	PHYNODEMETHOD(phynode_enable,		rk_usb2phy_enable),
99	PHYNODEMETHOD(phynode_usb_get_mode,	rk_usb2phy_get_mode),
100	PHYNODEMETHOD(phynode_usb_set_mode,	rk_usb2phy_set_mode),
101
102	PHYNODEMETHOD_END
103};
104
105DEFINE_CLASS_1(rk_usb2phy_phynode, rk_usb2phy_phynode_class,
106    rk_usb2phy_phynode_methods,
107    sizeof(struct phynode_usb_sc), phynode_usb_class);
108
109enum RK3399_USBPHY {
110	RK3399_USBPHY_HOST = 0,
111	RK3399_USBPHY_OTG,
112};
113
114static int
115rk_usb2phy_enable(struct phynode *phynode, bool enable)
116{
117	struct rk_usb2phy_softc *sc;
118	device_t dev;
119	intptr_t phy;
120	int error;
121
122	dev = phynode_get_device(phynode);
123	phy = phynode_get_id(phynode);
124	sc = device_get_softc(dev);
125
126	if (phy != RK3399_USBPHY_HOST)
127		return (ERANGE);
128
129	if (sc->phy_supply) {
130		if (enable)
131			error = regulator_enable(sc->phy_supply);
132		else
133			error = regulator_disable(sc->phy_supply);
134		if (error != 0) {
135			device_printf(dev, "Cannot %sable the regulator\n",
136			    enable ? "En" : "Dis");
137			goto fail;
138		}
139	}
140
141	return (0);
142fail:
143	return (ENXIO);
144}
145
146static int
147rk_usb2phy_get_mode(struct phynode *phynode, int *mode)
148{
149	struct rk_usb2phy_softc *sc;
150	intptr_t phy;
151	device_t dev;
152
153	dev = phynode_get_device(phynode);
154	phy = phynode_get_id(phynode);
155	sc = device_get_softc(dev);
156
157	if (phy != RK3399_USBPHY_HOST)
158		return (ERANGE);
159
160	*mode = sc->mode;
161
162	return (0);
163}
164
165static int
166rk_usb2phy_set_mode(struct phynode *phynode, int mode)
167{
168	struct rk_usb2phy_softc *sc;
169	intptr_t phy;
170	device_t dev;
171
172	dev = phynode_get_device(phynode);
173	phy = phynode_get_id(phynode);
174	sc = device_get_softc(dev);
175
176	if (phy != RK3399_USBPHY_HOST)
177		return (ERANGE);
178
179	sc->mode = mode;
180
181	return (0);
182}
183
184/* Clock class and method */
185struct rk_usb2phy_clk_sc {
186	device_t	clkdev;
187	struct syscon	*grf;
188	struct rk_usb2phy_regs	*regs;
189};
190
191static int
192rk_usb2phy_clk_init(struct clknode *clk, device_t dev)
193{
194
195	clknode_init_parent_idx(clk, 0);
196	return (0);
197}
198
199static int
200rk_usb2phy_clk_set_gate(struct clknode *clk, bool enable)
201{
202	struct rk_usb2phy_clk_sc *sc;
203
204	sc = clknode_get_softc(clk);
205
206	if (enable)
207		SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset,
208		    sc->regs->clk_ctl.enable_mask);
209	else
210		SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset,
211		    sc->regs->clk_ctl.disable_mask);
212	return (0);
213}
214
215static int
216rk_usb2phy_clk_recalc(struct clknode *clk, uint64_t *freq)
217{
218
219	*freq = 480000000;
220
221	return (0);
222}
223
224static clknode_method_t rk_usb2phy_clk_clknode_methods[] = {
225	/* Device interface */
226
227	CLKNODEMETHOD(clknode_init,		rk_usb2phy_clk_init),
228	CLKNODEMETHOD(clknode_set_gate,		rk_usb2phy_clk_set_gate),
229	CLKNODEMETHOD(clknode_recalc_freq,	rk_usb2phy_clk_recalc),
230	CLKNODEMETHOD_END
231};
232
233DEFINE_CLASS_1(rk_usb2phy_clk_clknode, rk_usb2phy_clk_clknode_class,
234    rk_usb2phy_clk_clknode_methods, sizeof(struct rk_usb2phy_clk_sc),
235    clknode_class);
236
237static int
238rk_usb2phy_clk_ofw_map(struct clkdom *clkdom, uint32_t ncells,
239    phandle_t *cells, struct clknode **clk)
240{
241
242	if (ncells != 0)
243		return (ERANGE);
244
245	*clk = clknode_find_by_id(clkdom, 0);
246
247	if (*clk == NULL)
248		return (ENXIO);
249	return (0);
250}
251
252static int
253rk_usb2phy_export_clock(struct rk_usb2phy_softc *devsc)
254{
255	struct clknode_init_def def;
256	struct rk_usb2phy_clk_sc *sc;
257	const char **clknames;
258	struct clkdom *clkdom;
259	struct clknode *clk;
260	clk_t clk_parent;
261	phandle_t node;
262	phandle_t regs[2];
263	int i, nclocks, ncells, error;
264
265	node = ofw_bus_get_node(devsc->dev);
266
267	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
268	    "#clock-cells", &ncells);
269	if (error != 0 || ncells != 1) {
270		device_printf(devsc->dev, "couldn't find parent clock\n");
271		return (ENXIO);
272	}
273
274	nclocks = ofw_bus_string_list_to_array(node, "clock-output-names",
275	    &clknames);
276	if (nclocks != 1)
277		return (ENXIO);
278
279	clkdom = clkdom_create(devsc->dev);
280	clkdom_set_ofw_mapper(clkdom, rk_usb2phy_clk_ofw_map);
281
282	memset(&def, 0, sizeof(def));
283	def.id = 0;
284	def.name = clknames[0];
285	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
286	for (i = 0; i < ncells; i++) {
287		error = clk_get_by_ofw_index(devsc->dev, 0, i, &clk_parent);
288		if (error != 0) {
289			device_printf(devsc->dev, "cannot get clock %d\n", error);
290			return (ENXIO);
291		}
292		def.parent_names[i] = clk_get_name(clk_parent);
293		clk_release(clk_parent);
294	}
295	def.parent_cnt = ncells;
296
297	clk = clknode_create(clkdom, &rk_usb2phy_clk_clknode_class, &def);
298	if (clk == NULL) {
299		device_printf(devsc->dev, "cannot create clknode\n");
300		return (ENXIO);
301	}
302
303	sc = clknode_get_softc(clk);
304	sc->clkdev = device_get_parent(devsc->dev);
305	sc->grf = devsc->grf;
306	sc->regs = (struct rk_usb2phy_regs *)ofw_bus_search_compatible(devsc->dev, compat_data)->ocd_data;
307	OF_getencprop(node, "reg", regs, sizeof(regs));
308	sc->regs->clk_ctl.offset = regs[0];
309	clknode_register(clkdom, clk);
310
311	if (clkdom_finit(clkdom) != 0) {
312		device_printf(devsc->dev, "cannot finalize clkdom initialization\n");
313		return (ENXIO);
314	}
315
316	if (bootverbose)
317		clkdom_dump(clkdom);
318
319	return (0);
320}
321
322static int
323rk_usb2phy_probe(device_t dev)
324{
325
326	if (!ofw_bus_status_okay(dev))
327		return (ENXIO);
328
329	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
330		return (ENXIO);
331
332	device_set_desc(dev, "Rockchip RK3399 USB2PHY");
333	return (BUS_PROBE_DEFAULT);
334}
335
336static int
337rk_usb2phy_attach(device_t dev)
338{
339	struct rk_usb2phy_softc *sc;
340	struct phynode_init_def phy_init;
341	struct phynode *phynode;
342	phandle_t node, host;
343	int err;
344
345	sc = device_get_softc(dev);
346	sc->dev = dev;
347	node = ofw_bus_get_node(dev);
348
349	if (syscon_get_handle_default(dev, &sc->grf) != 0) {
350		device_printf(dev, "Cannot get syscon handle\n");
351		return (ENXIO);
352	}
353
354	if (clk_get_by_ofw_name(dev, 0, "phyclk", &sc->clk) != 0) {
355		device_printf(dev, "Cannot get clock\n");
356		return (ENXIO);
357	}
358	err = clk_enable(sc->clk);
359	if (err != 0) {
360		device_printf(dev, "Could not enable clock %s\n",
361		    clk_get_name(sc->clk));
362		return (ENXIO);
363	}
364
365	err = rk_usb2phy_export_clock(sc);
366	if (err != 0)
367		return (err);
368
369	/* Only host is supported right now */
370
371	host = ofw_bus_find_child(node, "host-port");
372	if (host == 0) {
373		device_printf(dev, "Cannot find host-port child node\n");
374		return (ENXIO);
375	}
376
377	if (!ofw_bus_node_status_okay(host)) {
378		device_printf(dev, "host-port isn't okay\n");
379		return (0);
380	}
381
382	regulator_get_by_ofw_property(dev, host, "phy-supply", &sc->phy_supply);
383	phy_init.id = RK3399_USBPHY_HOST;
384	phy_init.ofw_node = host;
385	phynode = phynode_create(dev, &rk_usb2phy_phynode_class, &phy_init);
386	if (phynode == NULL) {
387		device_printf(dev, "failed to create host USB2PHY\n");
388		return (ENXIO);
389	}
390	if (phynode_register(phynode) == NULL) {
391		device_printf(dev, "failed to register host USB2PHY\n");
392		return (ENXIO);
393	}
394
395	OF_device_register_xref(OF_xref_from_node(host), dev);
396
397	return (0);
398}
399
400static device_method_t rk_usb2phy_methods[] = {
401	/* Device interface */
402	DEVMETHOD(device_probe,		rk_usb2phy_probe),
403	DEVMETHOD(device_attach,	rk_usb2phy_attach),
404
405	DEVMETHOD_END
406};
407
408static driver_t rk_usb2phy_driver = {
409	"rk_usb2phy",
410	rk_usb2phy_methods,
411	sizeof(struct rk_usb2phy_softc)
412};
413
414static devclass_t rk_usb2phy_devclass;
415EARLY_DRIVER_MODULE(rk_usb2phy, simplebus, rk_usb2phy_driver,
416    rk_usb2phy_devclass, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
417MODULE_VERSION(rk_usb2phy, 1);
418