Path: blob/main/sys/arm64/broadcom/brcmmdio/mdio_mux_iproc.c
39481 views
/*-1* Copyright (c) 2019 Juniper Networks, Inc.2* Copyright (c) 2019 Semihalf.3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED16* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE17* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,18* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES19* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR20* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,22* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN23* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE24* POSSIBILITY OF SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/bus.h>29#include <sys/kernel.h>30#include <sys/module.h>31#include <sys/rman.h>32#include <sys/systm.h>3334#include <dev/fdt/simplebus.h>35#include <dev/ofw/ofw_bus_subr.h>36#include <dev/ofw/ofw_bus.h>3738#include <machine/bus.h>39#include <machine/resource.h>4041#include "mdio_if.h"4243#define REG_BASE_RID 04445#define MDIO_RATE_ADJ_EXT_OFFSET 0x00046#define MDIO_RATE_ADJ_INT_OFFSET 0x00447#define MDIO_RATE_ADJ_DIVIDENT_SHIFT 164849#define MDIO_SCAN_CTRL_OFFSET 0x00850#define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR 285152#define MDIO_PARAM_OFFSET 0x23c53#define MDIO_PARAM_MIIM_CYCLE 2954#define MDIO_PARAM_INTERNAL_SEL 2555#define MDIO_PARAM_BUS_ID 2256#define MDIO_PARAM_C45_SEL 2157#define MDIO_PARAM_PHY_ID 1658#define MDIO_PARAM_PHY_DATA 05960#define MDIO_READ_OFFSET 0x24061#define MDIO_READ_DATA_MASK 0xffff62#define MDIO_ADDR_OFFSET 0x2446364#define MDIO_CTRL_OFFSET 0x24865#define MDIO_CTRL_WRITE_OP 0x166#define MDIO_CTRL_READ_OP 0x26768#define MDIO_STAT_OFFSET 0x24c69#define MDIO_STAT_DONE 17071#define BUS_MAX_ADDR 3272#define EXT_BUS_START_ADDR 167374#define MDIO_REG_ADDR_SPACE_SIZE 0x2507576#define MDIO_OPERATING_FREQUENCY 1100000077#define MDIO_RATE_ADJ_DIVIDENT 17879#define MII_ADDR_C45 (1<<30)8081static int brcm_iproc_mdio_probe(device_t);82static int brcm_iproc_mdio_attach(device_t);83static int brcm_iproc_mdio_detach(device_t);8485/* OFW bus interface */86struct brcm_mdio_ofw_devinfo {87struct ofw_bus_devinfo di_dinfo;88struct resource_list di_rl;89};9091struct brcm_iproc_mdio_softc {92struct simplebus_softc sbus;93device_t dev;94struct resource * reg_base;95uint32_t clock_rate;96};9798MALLOC_DEFINE(M_BRCM_IPROC_MDIO, "Broadcom IPROC MDIO",99"Broadcom IPROC MDIO dynamic memory");100101static int brcm_iproc_config(struct brcm_iproc_mdio_softc*);102static const struct ofw_bus_devinfo *103brcm_iproc_mdio_get_devinfo(device_t, device_t);104static int brcm_iproc_mdio_write_mux(device_t, int, int, int, int);105static int brcm_iproc_mdio_read_mux(device_t, int, int, int);106107static device_method_t brcm_iproc_mdio_fdt_methods[] = {108/* Device interface */109DEVMETHOD(device_probe, brcm_iproc_mdio_probe),110DEVMETHOD(device_attach, brcm_iproc_mdio_attach),111DEVMETHOD(device_detach, brcm_iproc_mdio_detach),112113/* Bus interface */114DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),115DEVMETHOD(bus_release_resource, bus_generic_release_resource),116DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),117118/* ofw_bus interface */119DEVMETHOD(ofw_bus_get_devinfo, brcm_iproc_mdio_get_devinfo),120DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),121DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),122DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),123DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),124DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),125126/* MDIO interface */127DEVMETHOD(mdio_writereg_mux, brcm_iproc_mdio_write_mux),128DEVMETHOD(mdio_readreg_mux, brcm_iproc_mdio_read_mux),129130/* End */131DEVMETHOD_END132};133134DEFINE_CLASS_0(brcm_iproc_mdio, brcm_iproc_mdio_driver,135brcm_iproc_mdio_fdt_methods, sizeof(struct brcm_iproc_mdio_softc));136137EARLY_DRIVER_MODULE(brcm_iproc_mdio, ofwbus, brcm_iproc_mdio_driver, 0, 0,138BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);139EARLY_DRIVER_MODULE(brcm_iproc_mdio, simplebus, brcm_iproc_mdio_driver, 0, 0,140BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);141142static struct ofw_compat_data mdio_compat_data[] = {143{"brcm,mdio-mux-iproc", true},144{NULL, false}145};146147static int148brcm_iproc_switch(struct brcm_iproc_mdio_softc *sc, int child)149{150uint32_t param, bus_id;151uint32_t bus_dir;152153/* select bus and its properties */154bus_dir = (child < EXT_BUS_START_ADDR);155bus_id = bus_dir ? child : (child - EXT_BUS_START_ADDR);156157param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL;158param |= (bus_id << MDIO_PARAM_BUS_ID);159160bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param);161162return (0);163}164165static int166iproc_mdio_wait_for_idle(struct brcm_iproc_mdio_softc *sc, uint32_t result)167{168unsigned int timeout = 1000; /* loop for 1s */169uint32_t val;170171do {172val = bus_read_4(sc->reg_base, MDIO_STAT_OFFSET);173if ((val & MDIO_STAT_DONE) == result)174return (0);175176pause("BRCM MDIO SLEEP", 1000 / hz);177} while (timeout--);178179return (ETIMEDOUT);180}181182/* start_miim_ops- Program and start MDIO transaction over mdio bus.183* @base: Base address184* @phyid: phyid of the selected bus.185* @reg: register offset to be read/written.186* @val :0 if read op else value to be written in @reg;187* @op: Operation that need to be carried out.188* MDIO_CTRL_READ_OP: Read transaction.189* MDIO_CTRL_WRITE_OP: Write transaction.190*191* Return value: Successful Read operation returns read reg values and write192* operation returns 0. Failure operation returns negative error code.193*/194static int195brcm_iproc_mdio_op(struct brcm_iproc_mdio_softc *sc,196uint16_t phyid, uint32_t reg, uint32_t val, uint32_t op)197{198uint32_t param;199int ret;200201bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, 0);202bus_read_4(sc->reg_base, MDIO_STAT_OFFSET);203ret = iproc_mdio_wait_for_idle(sc, 0);204if (ret)205goto err;206207param = bus_read_4(sc->reg_base, MDIO_PARAM_OFFSET);208param |= phyid << MDIO_PARAM_PHY_ID;209param |= val << MDIO_PARAM_PHY_DATA;210if (reg & MII_ADDR_C45)211param |= (1 << MDIO_PARAM_C45_SEL);212213bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param);214215bus_write_4(sc->reg_base, MDIO_ADDR_OFFSET, reg);216217bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, op);218219ret = iproc_mdio_wait_for_idle(sc, 1);220if (ret)221goto err;222223if (op == MDIO_CTRL_READ_OP)224ret = bus_read_4(sc->reg_base, MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK;225err:226return ret;227}228229static int230brcm_iproc_config(struct brcm_iproc_mdio_softc *sc)231{232uint32_t divisor;233uint32_t val;234235/* Disable external mdio master access */236val = bus_read_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET);237val |= 1 << MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR;238bus_write_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET, val);239240if (sc->clock_rate) {241/* use rate adjust regs to derrive the mdio's operating242* frequency from the specified core clock243*/244divisor = sc->clock_rate / MDIO_OPERATING_FREQUENCY;245divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1);246val = divisor;247val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT;248bus_write_4(sc->reg_base, MDIO_RATE_ADJ_EXT_OFFSET, val);249bus_write_4(sc->reg_base, MDIO_RATE_ADJ_INT_OFFSET, val);250}251252return (0);253}254255static int256brcm_iproc_mdio_write_mux(device_t dev, int bus, int phy, int reg, int val)257{258struct brcm_iproc_mdio_softc *sc;259260sc = device_get_softc(dev);261262if (brcm_iproc_switch(sc, bus) != 0) {263device_printf(dev, "Failed to set BUS MUX\n");264return (EINVAL);265}266267return (brcm_iproc_mdio_op(sc, phy, reg, val, MDIO_CTRL_WRITE_OP));268}269270static int271brcm_iproc_mdio_read_mux(device_t dev, int bus, int phy, int reg)272{273struct brcm_iproc_mdio_softc *sc;274275sc = device_get_softc(dev);276277if (brcm_iproc_switch(sc, bus) != 0) {278device_printf(dev, "Failed to set BUS MUX\n");279return (EINVAL);280}281282return (brcm_iproc_mdio_op(sc, phy, reg, 0, MDIO_CTRL_READ_OP));283}284285static int286brcm_iproc_mdio_probe(device_t dev)287{288289if (!ofw_bus_status_okay(dev))290return (ENXIO);291if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_data)292return (ENXIO);293294device_set_desc(dev, "Broadcom MDIO MUX driver");295return (BUS_PROBE_DEFAULT);296}297298static int299brcm_iproc_mdio_attach(device_t dev)300{301struct brcm_iproc_mdio_softc *sc;302phandle_t node, parent;303struct brcm_mdio_ofw_devinfo *di;304int rid;305device_t child;306307sc = device_get_softc(dev);308sc->dev = dev;309310/* Allocate memory resources */311rid = REG_BASE_RID;312sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,313RF_ACTIVE);314if (sc->reg_base == NULL) {315device_printf(dev, "Could not allocate memory\n");316return (ENXIO);317}318319/* Configure MDIO controlled */320if (brcm_iproc_config(sc) < 0) {321device_printf(dev, "Unable to initialize IPROC MDIO\n");322goto error;323}324325parent = ofw_bus_get_node(dev);326simplebus_init(dev, parent);327328/* Iterate through all bus subordinates */329for (node = OF_child(parent); node > 0; node = OF_peer(node)) {330/* Allocate and populate devinfo. */331di = malloc(sizeof(*di), M_BRCM_IPROC_MDIO, M_WAITOK | M_ZERO);332if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {333free(di, M_BRCM_IPROC_MDIO);334continue;335}336337/* Initialize and populate resource list. */338resource_list_init(&di->di_rl);339ofw_bus_reg_to_rl(dev, node, sc->sbus.acells, sc->sbus.scells,340&di->di_rl);341ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);342343/* Add newbus device for this FDT node */344child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);345if (child == NULL) {346printf("Failed to add child\n");347resource_list_free(&di->di_rl);348ofw_bus_gen_destroy_devinfo(&di->di_dinfo);349free(di, M_BRCM_IPROC_MDIO);350continue;351}352353device_set_ivars(child, di);354}355356/*357* Register device to this node/xref.358* Thanks to that we will be able to retrieve device_t structure359* while holding only node reference acquired from FDT.360*/361node = ofw_bus_get_node(dev);362OF_device_register_xref(OF_xref_from_node(node), dev);363364bus_attach_children(dev);365return (0);366367error:368brcm_iproc_mdio_detach(dev);369return (ENXIO);370}371372static const struct ofw_bus_devinfo *373brcm_iproc_mdio_get_devinfo(device_t bus __unused, device_t child)374{375struct brcm_mdio_ofw_devinfo *di;376377di = device_get_ivars(child);378return (&di->di_dinfo);379}380381static int382brcm_iproc_mdio_detach(device_t dev)383{384struct brcm_iproc_mdio_softc *sc;385386sc = device_get_softc(dev);387388if (sc->reg_base != NULL) {389bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,390sc->reg_base);391}392393return (0);394}395396397