xref: /openbsd/sys/dev/fdt/sfp.c (revision 5dea098c)
1 /* $OpenBSD: sfp.c,v 1.5 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3  * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/kernel.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 
24 #include <net/if.h>
25 
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <dev/i2c/i2cvar.h>
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_gpio.h>
32 #include <dev/ofw/ofw_misc.h>
33 
34 struct sfp_softc {
35 	struct device		 sc_dev;
36 	i2c_tag_t		 sc_tag;
37 	int			 sc_node;
38 
39 	uint32_t		*sc_mod_def0_gpio;
40 	int			 sc_mod_def0_gpio_len;
41 	uint32_t		*sc_tx_disable_gpio;
42 	int			 sc_tx_disable_gpio_len;
43 
44 	struct sfp_device	 sc_sd;
45 };
46 
47 int	 sfp_match(struct device *, void *, void *);
48 void	 sfp_attach(struct device *, struct device *, void *);
49 int	 sfp_detach(struct device *, int);
50 
51 int	 sfp_get_gpio(struct sfp_softc *, const char *, uint32_t **);
52 int	 sfp_gpio_enable(void *, int);
53 int	 sfp_i2c_get_sffpage(void *, struct if_sffpage *);
54 
55 const struct cfattach sfp_ca = {
56 	sizeof(struct sfp_softc), sfp_match, sfp_attach, sfp_detach,
57 };
58 
59 struct cfdriver sfp_cd = {
60 	NULL, "sfp", DV_DULL
61 };
62 
63 int
64 sfp_match(struct device *parent, void *match, void *aux)
65 {
66 	struct fdt_attach_args *faa = aux;
67 
68 	return (OF_is_compatible(faa->fa_node, "sff,sfp") ||
69 	    OF_is_compatible(faa->fa_node, "sff,sfp+"));
70 }
71 
72 void
73 sfp_attach(struct device *parent, struct device *self, void *aux)
74 {
75 	struct sfp_softc *sc = (struct sfp_softc *)self;
76 	struct fdt_attach_args *faa = aux;
77 
78 	sc->sc_node = faa->fa_node;
79 	sc->sc_tag = i2c_byphandle(OF_getpropint(sc->sc_node,
80 	    "i2c-bus", 0));
81 
82 	if (sc->sc_tag == NULL) {
83 		printf(": can't get i2c bus\n");
84 		return;
85 	}
86 
87 	printf("\n");
88 
89 	sc->sc_mod_def0_gpio_len =
90 	    sfp_get_gpio(sc, "mod-def0", &sc->sc_mod_def0_gpio);
91 	if (sc->sc_mod_def0_gpio) {
92 		gpio_controller_config_pin(sc->sc_mod_def0_gpio,
93 		    GPIO_CONFIG_INPUT);
94 	}
95 
96 	sc->sc_tx_disable_gpio_len =
97 	    sfp_get_gpio(sc, "tx-disable", &sc->sc_tx_disable_gpio);
98 	if (sc->sc_tx_disable_gpio) {
99 		gpio_controller_config_pin(sc->sc_tx_disable_gpio,
100 		    GPIO_CONFIG_OUTPUT);
101 	}
102 
103 	sc->sc_sd.sd_node = faa->fa_node;
104 	sc->sc_sd.sd_cookie = sc;
105 	sc->sc_sd.sd_enable = sfp_gpio_enable;
106 	sc->sc_sd.sd_get_sffpage = sfp_i2c_get_sffpage;
107 	sfp_register(&sc->sc_sd);
108 }
109 
110 int
111 sfp_detach(struct device *self, int flags)
112 {
113 	struct sfp_softc *sc = (struct sfp_softc *)self;
114 
115 	free(sc->sc_mod_def0_gpio, M_DEVBUF, sc->sc_mod_def0_gpio_len);
116 	free(sc->sc_tx_disable_gpio, M_DEVBUF, sc->sc_tx_disable_gpio_len);
117 	return 0;
118 }
119 
120 int
121 sfp_get_gpio(struct sfp_softc *sc, const char *name, uint32_t **gpio)
122 {
123 	char buf[64];
124 	int len;
125 
126 	snprintf(buf, sizeof(buf), "%s-gpios", name);
127 	len = OF_getproplen(sc->sc_node, buf);
128 	if (len <= 0) {
129 		snprintf(buf, sizeof(buf), "%s-gpio", name);
130 		len = OF_getproplen(sc->sc_node, buf);
131 		if (len <= 0)
132 			return len;
133 	}
134 	*gpio = malloc(len, M_DEVBUF, M_WAITOK);
135 	OF_getpropintarray(sc->sc_node, buf, *gpio, len);
136 	return len;
137 }
138 
139 int
140 sfp_gpio_enable(void *cookie, int enable)
141 {
142 	struct sfp_softc *sc = cookie;
143 
144 	if (sc->sc_tx_disable_gpio) {
145 		gpio_controller_set_pin(sc->sc_tx_disable_gpio, !enable);
146 		return 0;
147 	}
148 
149 	return ENXIO;
150 }
151 
152 int
153 sfp_i2c_get_sffpage(void *cookie, struct if_sffpage *sff)
154 {
155 	struct sfp_softc *sc = cookie;
156 	uint8_t reg = sff->sff_page;
157 
158 	if (sc->sc_mod_def0_gpio) {
159 		if (!gpio_controller_get_pin(sc->sc_mod_def0_gpio))
160 			return ENXIO;
161 	}
162 
163 	iic_acquire_bus(sc->sc_tag, 0);
164 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
165 	    sff->sff_addr >> 1, &reg, sizeof(reg),
166 	    sff->sff_data, sizeof(sff->sff_data), 0)) {
167 		printf("%s: cannot read register 0x%x\n",
168 		    sc->sc_dev.dv_xname, reg);
169 	}
170 	iic_release_bus(sc->sc_tag, 0);
171 
172 	return 0;
173 }
174