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
sfp_match(struct device * parent,void * match,void * aux)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
sfp_attach(struct device * parent,struct device * self,void * aux)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
sfp_detach(struct device * self,int flags)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
sfp_get_gpio(struct sfp_softc * sc,const char * name,uint32_t ** gpio)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
sfp_gpio_enable(void * cookie,int enable)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
sfp_i2c_get_sffpage(void * cookie,struct if_sffpage * sff)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, ®, 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