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, ®, 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