Add basic support for Freescale LX2160A SoC.
All peripherals but the network processor are supported.
This commit is contained in:
parent
2f3e7fb2cc
commit
6844eecfed
@ -147,7 +147,8 @@ static struct i2c_div_type vf610_div_table[] = {
|
||||
{ 0x2C, 576 }, { 0x2D, 640 }, { 0x2E, 768 }, { 0x32, 896 },
|
||||
{ 0x2F, 960 }, { 0x33, 1024 }, { 0x34, 1152 }, { 0x35, 1280 },
|
||||
{ 0x36, 1536 }, { 0x3A, 1792 }, { 0x37, 1920 }, { 0x3B, 2048 },
|
||||
{ 0x3C, 2304 }, { 0x3D, 2560 }, { 0x3E, 3072 }, { 0x3F, 3840 }
|
||||
{ 0x3C, 2304 }, { 0x3D, 2560 }, { 0x3E, 3072 }, { 0x3F, 3840 },
|
||||
{ 0x3F, 3840 }, { 0x7B, 4096 }, { 0x7D, 5120 }, { 0x7E, 6144 },
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -307,6 +308,14 @@ wait_for_icf(struct i2c_softc *sc)
|
||||
|
||||
return (IIC_ETIMEOUT);
|
||||
}
|
||||
/* Get ACK bit from last write */
|
||||
static bool
|
||||
tx_acked(struct i2c_softc *sc)
|
||||
{
|
||||
|
||||
return (READ1(sc, I2C_IBSR) & IBSR_RXAK) ? false : true;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_repeated_start(device_t dev, u_char slave, int timeout)
|
||||
@ -342,6 +351,12 @@ i2c_repeated_start(device_t dev, u_char slave, int timeout)
|
||||
|
||||
error = wait_for_iif(sc);
|
||||
|
||||
if (!tx_acked(sc)) {
|
||||
vf_i2c_dbg(sc,
|
||||
"cant i2c start: missing ACK after slave addres\n");
|
||||
return (IIC_ENOACK);
|
||||
}
|
||||
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (error != 0)
|
||||
@ -384,12 +399,18 @@ i2c_start(device_t dev, u_char slave, int timeout)
|
||||
WRITE1(sc, I2C_IBDR, slave);
|
||||
|
||||
error = wait_for_iif(sc);
|
||||
|
||||
mtx_unlock(&sc->mutex);
|
||||
if (error != 0) {
|
||||
mtx_unlock(&sc->mutex);
|
||||
vf_i2c_dbg(sc, "cant i2c start: iif error\n");
|
||||
return (error);
|
||||
}
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (!tx_acked(sc)) {
|
||||
vf_i2c_dbg(sc,
|
||||
"cant i2c start: missing QACK after slave addres\n");
|
||||
return (IIC_ENOACK);
|
||||
}
|
||||
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
@ -568,10 +589,15 @@ i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (!tx_acked(sc) && (*sent = (len - 2)) ){
|
||||
mtx_unlock(&sc->mutex);
|
||||
vf_i2c_dbg(sc, "no ACK on %d write\n", *sent);
|
||||
return (IIC_ENOACK);
|
||||
}
|
||||
|
||||
(*sent)++;
|
||||
}
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
@ -600,14 +626,8 @@ static device_method_t i2c_methods[] = {
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t i2c_driver = {
|
||||
"i2c",
|
||||
i2c_methods,
|
||||
sizeof(struct i2c_softc),
|
||||
};
|
||||
|
||||
static devclass_t i2c_devclass;
|
||||
|
||||
DRIVER_MODULE(i2c, simplebus, i2c_driver, i2c_devclass, 0, 0);
|
||||
static DEFINE_CLASS_0(i2c, i2c_driver, i2c_methods, sizeof(struct i2c_softc));
|
||||
DRIVER_MODULE(vybrid_i2c, simplebus, i2c_driver, i2c_devclass, 0, 0);
|
||||
DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0);
|
||||
DRIVER_MODULE(ofw_iicbus, i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0);
|
||||
|
211
sys/arm64/qoriq/clk/lx2160a_clkgen.c
Normal file
211
sys/arm64/qoriq/clk/lx2160a_clkgen.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* Clock driver for LX2160A SoC.
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/fdt/simplebus.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/extres/clk/clk_fixed.h>
|
||||
|
||||
#include <arm64/qoriq/clk/qoriq_clkgen.h>
|
||||
|
||||
#define PLL(_id1, _id2, cname, o, d) \
|
||||
{ \
|
||||
.clkdef.id = QORIQ_CLK_ID(_id1, _id2), \
|
||||
.clkdef.name = cname, \
|
||||
.clkdef.flags = 0, \
|
||||
.offset = o, \
|
||||
.shift = 1, \
|
||||
.mask = 0xFE, \
|
||||
.dividers = d, \
|
||||
.flags = QORIQ_CLK_PLL_HAS_KILL_BIT, \
|
||||
}
|
||||
|
||||
static const uint8_t plt_divs[] =
|
||||
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0};
|
||||
static const uint8_t cga_divs[] = {2, 4, 0};
|
||||
static const uint8_t cgb_divs[] = {2, 3, 4, 0};
|
||||
|
||||
static struct qoriq_clk_pll_def pltfrm_pll =
|
||||
PLL(QORIQ_TYPE_PLATFORM_PLL, 0, "platform_pll", 0x60080, plt_divs);
|
||||
static struct qoriq_clk_pll_def cga_pll1 =
|
||||
PLL(QORIQ_TYPE_INTERNAL, 0, "cga_pll1", 0x80, cga_divs);
|
||||
static struct qoriq_clk_pll_def cga_pll2 =
|
||||
PLL(QORIQ_TYPE_INTERNAL, 0, "cga_pll2", 0xA0, cga_divs);
|
||||
static struct qoriq_clk_pll_def cgb_pll1 =
|
||||
PLL(QORIQ_TYPE_INTERNAL, 0, "cgb_pll1", 0x10080, cgb_divs);
|
||||
static struct qoriq_clk_pll_def cgb_pll2 =
|
||||
PLL(QORIQ_TYPE_INTERNAL, 0, "cgb_pll2", 0x100A0, cgb_divs);
|
||||
|
||||
static struct qoriq_clk_pll_def *cg_plls[] = {
|
||||
&cga_pll1,
|
||||
&cga_pll2,
|
||||
&cgb_pll1,
|
||||
&cgb_pll2,
|
||||
};
|
||||
|
||||
#if 0
|
||||
static struct qoriq_clk_pll_def *cg_plls[] = {
|
||||
&(struct qoriq_clk_pll_def)
|
||||
{PLL(QORIQ_TYPE_INTERNAL, 0, "cga_pll1", 0x80, cg_divs)},
|
||||
&(struct qoriq_clk_pll_def)
|
||||
{PLL(QORIQ_TYPE_INTERNAL, 0, "cga_pll2", 0xA0, cg_divs)},
|
||||
&(struct qoriq_clk_pll_def)
|
||||
{PLL(QORIQ_TYPE_INTERNAL, 0, "cgb_pll1", 0x10080, cg_divs)},
|
||||
&(struct qoriq_clk_pll_def)
|
||||
{PLL(QORIQ_TYPE_INTERNAL, 0, "cgb_pll2", 0x100A0, cg_divs)},
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char *cmuxa_plist[] = {
|
||||
"cga_pll1",
|
||||
"cga_pll1_div2",
|
||||
"cga_pll1_div4",
|
||||
NULL,
|
||||
"cga_pll2",
|
||||
"cga_pll2_div2",
|
||||
"cga_pll2_div4",
|
||||
};
|
||||
|
||||
static const char *cmuxb_plist[] = {
|
||||
"cgb_pll1",
|
||||
"cgb_pll1_div2",
|
||||
"cgb_pll1_div4",
|
||||
NULL,
|
||||
"cgb_pll2",
|
||||
"cgb_pll2_div2",
|
||||
"cgb_pll2_div4",
|
||||
};
|
||||
|
||||
#define MUX(_id1, _id2, cname, plist, o) \
|
||||
{ \
|
||||
.clkdef.id = QORIQ_CLK_ID(_id1, _id2), \
|
||||
.clkdef.name = cname, \
|
||||
.clkdef.parent_names = plist, \
|
||||
.clkdef.parent_cnt = nitems(plist), \
|
||||
.clkdef.flags = 0, \
|
||||
.offset = o, \
|
||||
.width = 4, \
|
||||
.shift = 27, \
|
||||
.mux_flags = 0, \
|
||||
}
|
||||
static struct clk_mux_def cmux0 =
|
||||
MUX(QORIQ_TYPE_CMUX, 0, "cg-cmux0", cmuxa_plist, 0x70000);
|
||||
static struct clk_mux_def cmux1 =
|
||||
MUX(QORIQ_TYPE_CMUX, 1, "cg-cmux1", cmuxa_plist, 0x70020);
|
||||
static struct clk_mux_def cmux2 =
|
||||
MUX(QORIQ_TYPE_CMUX, 2, "cg-cmux2", cmuxa_plist, 0x70040);
|
||||
static struct clk_mux_def cmux3 =
|
||||
MUX(QORIQ_TYPE_CMUX, 3, "cg-cmux3", cmuxa_plist, 0x70060);
|
||||
static struct clk_mux_def cmux4 =
|
||||
MUX(QORIQ_TYPE_CMUX, 4, "cg-cmux4", cmuxb_plist, 0x70080);
|
||||
static struct clk_mux_def cmux5 =
|
||||
MUX(QORIQ_TYPE_CMUX, 5, "cg-cmux5", cmuxb_plist, 0x700A0);
|
||||
static struct clk_mux_def cmux6 =
|
||||
MUX(QORIQ_TYPE_CMUX, 6, "cg-cmux6", cmuxb_plist, 0x700C0);
|
||||
static struct clk_mux_def cmux7 =
|
||||
MUX(QORIQ_TYPE_CMUX, 7, "cg-cmux7", cmuxb_plist, 0x700E0);
|
||||
|
||||
static struct clk_mux_def *mux_nodes[] = {
|
||||
&cmux0,
|
||||
&cmux1,
|
||||
&cmux2,
|
||||
&cmux3,
|
||||
&cmux4,
|
||||
&cmux5,
|
||||
&cmux6,
|
||||
&cmux7,
|
||||
};
|
||||
|
||||
static int
|
||||
lx2160a_clkgen_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if(!ofw_bus_is_compatible(dev, "fsl,lx2160a-clockgen"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "LX2160A clockgen");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
lx2160a_clkgen_attach(device_t dev)
|
||||
{
|
||||
struct qoriq_clkgen_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
sc->pltfrm_pll_def = &pltfrm_pll;
|
||||
sc->cga_pll = cg_plls;
|
||||
sc->cga_pll_num = nitems(cg_plls);
|
||||
sc->mux = mux_nodes;
|
||||
sc->mux_num = nitems(mux_nodes);
|
||||
sc->flags = QORIQ_LITTLE_ENDIAN;
|
||||
|
||||
rv = qoriq_clkgen_attach(dev);
|
||||
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x00080, bus_read_4(sc->res, 0x00080));
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x000A0, bus_read_4(sc->res, 0x000A0));
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x10080, bus_read_4(sc->res, 0x10080));
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x100A0, bus_read_4(sc->res, 0x100A0));
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x60080, bus_read_4(sc->res, 0x60080));
|
||||
printf(" %s: offset: 0x%08X, val: 0x%08X\n", __func__, 0x600A0, bus_read_4(sc->res, 0x600A0));
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static device_method_t lx2160a_clkgen_methods[] = {
|
||||
DEVMETHOD(device_probe, lx2160a_clkgen_probe),
|
||||
DEVMETHOD(device_attach, lx2160a_clkgen_attach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static devclass_t lx2160a_clkgen_devclass;
|
||||
|
||||
DEFINE_CLASS_1(lx2160a_clkgen, lx2160a_clkgen_driver, lx2160a_clkgen_methods,
|
||||
sizeof(struct qoriq_clkgen_softc), qoriq_clkgen_driver);
|
||||
EARLY_DRIVER_MODULE(lx2160a_clkgen, simplebus, lx2160a_clkgen_driver,
|
||||
lx2160a_clkgen_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
|
256
sys/arm64/qoriq/qoriq_dw_pci.c
Normal file
256
sys/arm64/qoriq/qoriq_dw_pci.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Layerscape DesignWare PCIe driver */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/devmap.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/ofw/ofw_pci.h>
|
||||
#include <dev/ofw/ofwpci.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcib_private.h>
|
||||
#include <dev/pci/pci_dw.h>
|
||||
|
||||
#include "pcib_if.h"
|
||||
#include "pci_dw_if.h"
|
||||
|
||||
#define PCIE_ABSERR 0x8D0
|
||||
|
||||
struct qoriq_dw_pci_cfg {
|
||||
uint32_t pex_pf0_dgb; /* offset of PEX_PF0_DBG register */
|
||||
uint32_t ltssm_bit; /* LSB bit of of LTSSM state field */
|
||||
};
|
||||
|
||||
struct qorif_dw_pci_softc {
|
||||
struct pci_dw_softc dw_sc;
|
||||
device_t dev;
|
||||
phandle_t node;
|
||||
struct resource *irq_res;
|
||||
void *intr_cookie;
|
||||
struct qoriq_dw_pci_cfg *soc_cfg;
|
||||
|
||||
};
|
||||
|
||||
static struct qoriq_dw_pci_cfg ls1043_cfg = {
|
||||
.pex_pf0_dgb = 0x10000 + 0x7FC,
|
||||
.ltssm_bit = 24,
|
||||
};
|
||||
|
||||
static struct qoriq_dw_pci_cfg ls1012_cfg = {
|
||||
.pex_pf0_dgb = 0x80000 + 0x407FC,
|
||||
.ltssm_bit = 24,
|
||||
};
|
||||
|
||||
static struct qoriq_dw_pci_cfg ls2080_cfg = {
|
||||
.pex_pf0_dgb = 0x80000 + 0x7FC,
|
||||
.ltssm_bit = 0,
|
||||
};
|
||||
|
||||
static struct qoriq_dw_pci_cfg ls2028_cfg = {
|
||||
.pex_pf0_dgb = 0x80000 + 0x407FC,
|
||||
.ltssm_bit = 0,
|
||||
};
|
||||
|
||||
|
||||
/* Compatible devices. */
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{"fsl,ls1012a-pcie", (uintptr_t)&ls1012_cfg},
|
||||
{"fsl,ls1028a-pcie", (uintptr_t)&ls2028_cfg},
|
||||
{"fsl,ls1043a-pcie", (uintptr_t)&ls1043_cfg},
|
||||
{"fsl,ls1046a-pcie", (uintptr_t)&ls1012_cfg},
|
||||
{"fsl,ls2080a-pcie", (uintptr_t)&ls2080_cfg},
|
||||
{"fsl,ls2085a-pcie", (uintptr_t)&ls2080_cfg},
|
||||
{"fsl,ls2088a-pcie", (uintptr_t)&ls2028_cfg},
|
||||
{"fsl,ls1088a-pcie", (uintptr_t)&ls2028_cfg},
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static void
|
||||
qorif_dw_pci_dbi_protect(struct qorif_dw_pci_softc *sc, bool protect)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
reg = pci_dw_dbi_rd4(sc->dev, DW_MISC_CONTROL_1);
|
||||
if (protect)
|
||||
reg &= ~DBI_RO_WR_EN;
|
||||
else
|
||||
reg |= DBI_RO_WR_EN;
|
||||
pci_dw_dbi_wr4(sc->dev, DW_MISC_CONTROL_1, reg);
|
||||
}
|
||||
|
||||
static int qorif_dw_pci_intr(void *arg)
|
||||
{
|
||||
#if 0
|
||||
struct qorif_dw_pci_softc *sc = arg;
|
||||
uint32_t cause1, cause2;
|
||||
|
||||
/* Ack all interrups */
|
||||
cause1 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE1);
|
||||
cause2 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE2);
|
||||
|
||||
pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE1, cause1);
|
||||
pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE2, cause2);
|
||||
#endif
|
||||
return (FILTER_HANDLED);
|
||||
}
|
||||
|
||||
static int
|
||||
qorif_dw_pci_get_link(device_t dev, bool *status)
|
||||
{
|
||||
struct qorif_dw_pci_softc *sc;
|
||||
uint32_t reg;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
reg = pci_dw_dbi_rd4(sc->dev, sc->soc_cfg->pex_pf0_dgb);
|
||||
reg >>= sc->soc_cfg->ltssm_bit;
|
||||
reg &= 0x3F;
|
||||
*status = (reg = 0x11) ? true: false;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
qorif_dw_pci_init(struct qorif_dw_pci_softc *sc)
|
||||
{
|
||||
|
||||
// ls_pcie_disable_outbound_atus(pcie);
|
||||
|
||||
/* Forward error response */
|
||||
pci_dw_dbi_wr4(sc->dev, PCIE_ABSERR, 0x9401);
|
||||
|
||||
qorif_dw_pci_dbi_protect(sc, true);
|
||||
pci_dw_dbi_wr1(sc->dev, PCIR_HDRTYPE, 1);
|
||||
qorif_dw_pci_dbi_protect(sc, false);
|
||||
|
||||
// ls_pcie_drop_msg_tlp(pcie);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
qorif_dw_pci_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "NPX Layaerscape PCI-E Controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
qorif_dw_pci_attach(device_t dev)
|
||||
{
|
||||
struct qorif_dw_pci_softc *sc;
|
||||
phandle_t node;
|
||||
int rv;
|
||||
int rid;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
node = ofw_bus_get_node(dev);
|
||||
sc->dev = dev;
|
||||
sc->node = node;
|
||||
sc->soc_cfg = (struct qoriq_dw_pci_cfg *)
|
||||
ofw_bus_search_compatible(dev, compat_data)->ocd_data;
|
||||
|
||||
rid = 0;
|
||||
sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->dw_sc.dbi_res == NULL) {
|
||||
device_printf(dev, "Cannot allocate DBI memory\n");
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* PCI interrupt */
|
||||
rid = 0;
|
||||
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
||||
RF_ACTIVE | RF_SHAREABLE);
|
||||
if (sc->irq_res == NULL) {
|
||||
device_printf(dev, "Cannot allocate IRQ resources\n");
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = pci_dw_init(dev);
|
||||
if (rv != 0)
|
||||
goto out;
|
||||
|
||||
qorif_dw_pci_init(sc);
|
||||
|
||||
/* Setup interrupt */
|
||||
if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
|
||||
qorif_dw_pci_intr, NULL, sc, &sc->intr_cookie)) {
|
||||
device_printf(dev, "cannot setup interrupt handler\n");
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return (bus_generic_attach(dev));
|
||||
out:
|
||||
/* XXX Cleanup */
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static device_method_t qorif_dw_pci_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, qorif_dw_pci_probe),
|
||||
DEVMETHOD(device_attach, qorif_dw_pci_attach),
|
||||
|
||||
DEVMETHOD(pci_dw_get_link, qorif_dw_pci_get_link),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(pcib, qorif_dw_pci_driver, qorif_dw_pci_methods,
|
||||
sizeof(struct qorif_dw_pci_softc), pci_dw_driver);
|
||||
static devclass_t qorif_dw_pci_devclass;
|
||||
DRIVER_MODULE( qorif_dw_pci, simplebus, qorif_dw_pci_driver, qorif_dw_pci_devclass,
|
||||
NULL, NULL);
|
406
sys/arm64/qoriq/qoriq_therm.c
Normal file
406
sys/arm64/qoriq/qoriq_therm.c
Normal file
@ -0,0 +1,406 @@
|
||||
/*-
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* Thermometer driver for QorIQ SoCs.
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include "qoriq_therm_if.h"
|
||||
|
||||
#define TMU_TMR 0x00
|
||||
#define TMU_TSR 0x04
|
||||
#define TMUV1_TMTMIR 0x08
|
||||
#define TMUV2_TMSR 0x08
|
||||
#define TMUV2_TMTMIR 0x0C
|
||||
#define TMU_TIER 0x20
|
||||
#define TMU_TTCFGR 0x80
|
||||
#define TMU_TSCFGR 0x84
|
||||
#define TMU_TRITSR(x) (0x100 + (16 * (x)))
|
||||
#define TMU_TRITSR_VALID (1U << 31)
|
||||
#define TMUV2_TMSAR(x) (0x304 + (16 * (x)))
|
||||
#define TMU_VERSION 0xBF8 /* not in TRM */
|
||||
#define TMUV2_TEUMR(x) (0xF00 + (4 * (x)))
|
||||
#define TMU_TTRCR(x) (0xF10 + (4 * (x)))
|
||||
|
||||
|
||||
struct tsensor {
|
||||
int site_id;
|
||||
char *name;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct qoriq_therm_softc {
|
||||
device_t dev;
|
||||
struct resource *mem_res;
|
||||
struct resource *irq_res;
|
||||
void *irq_ih;
|
||||
int ntsensors;
|
||||
struct tsensor *tsensors;
|
||||
bool little_endian;
|
||||
clk_t clk;
|
||||
int ver;
|
||||
};
|
||||
|
||||
static struct sysctl_ctx_list qoriq_therm_sysctl_ctx;
|
||||
|
||||
struct tsensor default_sensors[] =
|
||||
{
|
||||
{ 0, "site0", 0},
|
||||
{ 1, "site1", 1},
|
||||
{ 2, "site2", 2},
|
||||
{ 3, "site3", 3},
|
||||
{ 4, "site4", 4},
|
||||
{ 5, "site5", 5},
|
||||
{ 6, "site6", 6},
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{"fsl,qoriq-tmu", 1},
|
||||
{"fsl,imx8mq-tmu", 1},
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static inline void
|
||||
WR4(struct qoriq_therm_softc *sc, bus_size_t addr, uint32_t val)
|
||||
{
|
||||
|
||||
val = sc->little_endian ? htole32(val): htobe32(val);
|
||||
bus_write_4(sc->mem_res, addr, val);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
RD4(struct qoriq_therm_softc *sc, bus_size_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = bus_read_4(sc->mem_res, addr);
|
||||
return (sc->little_endian ? le32toh(val): be32toh(val));
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_read_temp(struct qoriq_therm_softc *sc, struct tsensor *sensor,
|
||||
int *temp)
|
||||
{
|
||||
int timeout;
|
||||
uint32_t val;
|
||||
|
||||
/* wait for valid sample */
|
||||
for (timeout = 1000; timeout > 0; timeout--) {
|
||||
val = RD4(sc, TMU_TRITSR(sensor->site_id));
|
||||
if (val & TMU_TRITSR_VALID)
|
||||
break;
|
||||
DELAY(100);
|
||||
}
|
||||
if (timeout <= 0)
|
||||
device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
|
||||
|
||||
*temp = (int)(val & 0x1FF) * 1000;
|
||||
if (sc->ver == 1)
|
||||
*temp = (int)(val & 0xFF) * 1000;
|
||||
else
|
||||
*temp = (int)(val & 0x1FF) * 1000 - 273100;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
|
||||
{
|
||||
struct qoriq_therm_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (id >= sc->ntsensors)
|
||||
return (ERANGE);
|
||||
return(qoriq_therm_read_temp(sc, sc->tsensors + id, val));
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct qoriq_therm_softc *sc;
|
||||
int val;
|
||||
int rv;
|
||||
int id;
|
||||
|
||||
/* Write request */
|
||||
if (req->newptr != NULL)
|
||||
return (EINVAL);
|
||||
|
||||
sc = arg1;
|
||||
id = arg2;
|
||||
|
||||
if (id >= sc->ntsensors)
|
||||
return (ERANGE);
|
||||
rv = qoriq_therm_read_temp(sc, sc->tsensors + id, &val);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
|
||||
val = val / 100;
|
||||
val += 2731;
|
||||
rv = sysctl_handle_int(oidp, &val, 0, req);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_init_sysctl(struct qoriq_therm_softc *sc)
|
||||
{
|
||||
int i;
|
||||
struct sysctl_oid *oid, *tmp;
|
||||
|
||||
sysctl_ctx_init(&qoriq_therm_sysctl_ctx);
|
||||
/* create node for hw.temp */
|
||||
oid = SYSCTL_ADD_NODE(&qoriq_therm_sysctl_ctx,
|
||||
SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
|
||||
CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
|
||||
if (oid == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* add sensors */
|
||||
for (i = sc->ntsensors - 1; i >= 0; i--) {
|
||||
tmp = SYSCTL_ADD_PROC(&qoriq_therm_sysctl_ctx,
|
||||
SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name,
|
||||
CTLTYPE_INT | CTLFLAG_RD , sc, i,
|
||||
qoriq_therm_sysctl_temperature, "IK", "SoC Temperature");
|
||||
if (tmp == NULL)
|
||||
return (ENXIO);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_fdt_calib(struct qoriq_therm_softc *sc, phandle_t node)
|
||||
{
|
||||
int nranges, ncalibs, i;
|
||||
int *ranges, *calibs;
|
||||
|
||||
/* initialize temperature range control registes */
|
||||
nranges = OF_getencprop_alloc_multi(node, "fsl,tmu-range",
|
||||
sizeof(*ranges), (void **)&ranges);
|
||||
if (nranges < 2 || nranges > 4) {
|
||||
device_printf(sc->dev, "Invalid 'tmu-range' property\n");
|
||||
return (ERANGE);
|
||||
}
|
||||
for (i = 0; i < nranges; i++) {
|
||||
WR4(sc, TMU_TTRCR(i), ranges[i]);
|
||||
}
|
||||
|
||||
/* initialize calibration data for above ranges */
|
||||
ncalibs = OF_getencprop_alloc_multi(node, "fsl,tmu-calibration",
|
||||
sizeof(*calibs),(void **)&calibs);
|
||||
if (ncalibs <= 0 || (ncalibs % 2) != 0) {
|
||||
device_printf(sc->dev, "Invalid 'tmu-calibration' property\n");
|
||||
return (ERANGE);
|
||||
}
|
||||
for (i = 0; i < ncalibs; i +=2) {
|
||||
WR4(sc, TMU_TTCFGR, calibs[i]);
|
||||
WR4(sc, TMU_TSCFGR, calibs[i + 1]);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "QorIQ temperature sensors");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_attach(device_t dev)
|
||||
{
|
||||
struct qoriq_therm_softc *sc;
|
||||
phandle_t node;
|
||||
uint32_t sites;
|
||||
int rid, rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
node = ofw_bus_get_node(sc->dev);
|
||||
sc->little_endian = OF_hasprop(node, "little-endian");
|
||||
|
||||
rid = 0;
|
||||
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->mem_res == NULL) {
|
||||
device_printf(dev, "Cannot allocate memory resources\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rid = 0;
|
||||
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
|
||||
if (sc->irq_res == NULL) {
|
||||
device_printf(dev, "Cannot allocate IRQ resources\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
|
||||
qoriq_therm_intr, NULL, sc, &sc->irq_ih))) {
|
||||
device_printf(dev,
|
||||
"WARNING: unable to register interrupt handler\n");
|
||||
goto fail;
|
||||
}
|
||||
*/
|
||||
rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
|
||||
if (rv != 0 && rv != ENOENT) {
|
||||
device_printf(dev, "Cannot get clock: %d %d\n", rv, ENOENT);
|
||||
goto fail;
|
||||
}
|
||||
if (sc->clk != NULL) {
|
||||
rv = clk_enable(sc->clk);
|
||||
if (rv != 0) {
|
||||
device_printf(dev, "Cannot enable clock: %d\n", rv);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
sc->ver = (RD4(sc, TMU_VERSION) >> 8) & 0xFF;
|
||||
|
||||
/* XXX add per SoC customization */
|
||||
sc->ntsensors = nitems(default_sensors);
|
||||
sc->tsensors = default_sensors;
|
||||
|
||||
/* stop monitoring */
|
||||
WR4(sc, TMU_TMR, 0);
|
||||
RD4(sc, TMU_TMR);
|
||||
|
||||
/* disable all interrupts */
|
||||
WR4(sc, TMU_TIER, 0);
|
||||
|
||||
/* setup measurement interval */
|
||||
if (sc->ver == 1) {
|
||||
WR4(sc, TMUV1_TMTMIR, 0x0F);
|
||||
} else {
|
||||
WR4(sc, TMUV2_TMTMIR, 0x0F); /* disable */
|
||||
/* these registers are not of settings is not in TRM */
|
||||
WR4(sc, TMUV2_TEUMR(0), 0x51009c00);
|
||||
for (int i = 0; i < 7; i++)
|
||||
WR4(sc, TMUV2_TMSAR(0), 0xE);
|
||||
}
|
||||
|
||||
/* prepare calibration tables */
|
||||
rv = qoriq_therm_fdt_calib(sc, node);
|
||||
if (rv != 0) {
|
||||
device_printf(sc->dev,
|
||||
"Cannot initialize calibration tables\n");
|
||||
goto fail;
|
||||
}
|
||||
/* start monitoring */
|
||||
sites = (1U << sc->ntsensors) - 1;
|
||||
if (sc->ver == 1) {
|
||||
WR4(sc, TMU_TMR, 0x8C000000 | sites);
|
||||
} else {
|
||||
WR4(sc, TMUV2_TMSR, sites);
|
||||
WR4(sc, TMU_TMR, 0x83000000);
|
||||
}
|
||||
|
||||
rv = qoriq_therm_init_sysctl(sc);
|
||||
if (rv != 0) {
|
||||
device_printf(sc->dev, "Cannot initialize sysctls\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
OF_device_register_xref(OF_xref_from_node(node), dev);
|
||||
return (bus_generic_attach(dev));
|
||||
|
||||
fail:
|
||||
if (sc->irq_ih != NULL)
|
||||
bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
|
||||
sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
|
||||
if (sc->clk != NULL)
|
||||
clk_release(sc->clk);
|
||||
if (sc->irq_res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
|
||||
if (sc->mem_res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
qoriq_therm_detach(device_t dev)
|
||||
{
|
||||
struct qoriq_therm_softc *sc;
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (sc->irq_ih != NULL)
|
||||
bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
|
||||
sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
|
||||
if (sc->clk != NULL)
|
||||
clk_release(sc->clk);
|
||||
if (sc->irq_res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
|
||||
if (sc->mem_res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t qoriq_qoriq_therm_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, qoriq_therm_probe),
|
||||
DEVMETHOD(device_attach, qoriq_therm_attach),
|
||||
DEVMETHOD(device_detach, qoriq_therm_detach),
|
||||
|
||||
/* SOCTHERM interface */
|
||||
DEVMETHOD(qoriq_therm_get_temperature, qoriq_therm_get_temp),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static devclass_t qoriq_qoriq_therm_devclass;
|
||||
static DEFINE_CLASS_0(soctherm, qoriq_qoriq_therm_driver, qoriq_qoriq_therm_methods,
|
||||
sizeof(struct qoriq_therm_softc));
|
||||
DRIVER_MODULE(qoriq_soctherm, simplebus, qoriq_qoriq_therm_driver,
|
||||
qoriq_qoriq_therm_devclass, NULL, NULL);
|
44
sys/arm64/qoriq/qoriq_therm_if.m
Normal file
44
sys/arm64/qoriq/qoriq_therm_if.m
Normal file
@ -0,0 +1,44 @@
|
||||
#-
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
#
|
||||
# Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
INTERFACE qoriq_therm;
|
||||
|
||||
|
||||
/**
|
||||
* Read temperature
|
||||
*/
|
||||
METHOD int get_temperature{
|
||||
device_t dev;
|
||||
device_t consumer;
|
||||
uintptr_t id;
|
||||
int *val;
|
||||
};
|
@ -1854,6 +1854,7 @@ dev/iicbus/iicoc_fdt.c optional iicoc ext_resources fdt
|
||||
dev/iicbus/iicoc_pci.c optional iicoc pci
|
||||
dev/iicbus/isl12xx.c optional isl12xx
|
||||
dev/iicbus/lm75.c optional lm75
|
||||
dev/iicbus/mux/pca9547.c optional pca9547 iicmux fdt
|
||||
dev/iicbus/mux/iicmux.c optional iicmux
|
||||
dev/iicbus/mux/iicmux_if.m optional iicmux
|
||||
dev/iicbus/mux/iic_gpiomux.c optional iic_gpiomux fdt
|
||||
|
@ -217,7 +217,11 @@ arm64/iommu/smmu.c optional iommu
|
||||
arm64/iommu/smmu_acpi.c optional acpi iommu
|
||||
arm64/iommu/smmu_quirks.c optional iommu
|
||||
arm64/qoriq/ls1046_gpio.c optional ls1046_gpio gpio fdt SOC_NXP_LS
|
||||
arm64/qoriq/qoriq_dw_pci.c optional pci fdt SOC_NXP_LS
|
||||
arm64/qoriq/qoriq_therm.c optional pci fdt SOC_NXP_LS
|
||||
arm64/qoriq/qoriq_therm_if.m optional pci fdt SOC_NXP_LS
|
||||
arm64/qoriq/clk/ls1046a_clkgen.c optional clk SOC_NXP_LS
|
||||
arm64/qoriq/clk/lx2160a_clkgen.c optional clk SOC_NXP_LS
|
||||
arm64/qoriq/clk/qoriq_clk_pll.c optional clk SOC_NXP_LS
|
||||
arm64/qoriq/clk/qoriq_clkgen.c optional clk SOC_NXP_LS
|
||||
arm64/qualcomm/qcom_gcc.c optional qcom_gcc fdt
|
||||
|
@ -57,8 +57,11 @@ __FBSDID("$FreeBSD$");
|
||||
#define AHCI_FSL_REG_PHY3 0xb0
|
||||
#define AHCI_FSL_REG_PHY4 0xb4
|
||||
#define AHCI_FSL_REG_PHY5 0xb8
|
||||
#define AHCI_FSL_REG_AXICC 0xbc
|
||||
#define AHCI_FSL_REG_PTC 0xc8
|
||||
|
||||
#define AHCI_FSL_LS1021A_AXICC 0xc0
|
||||
|
||||
#define AHCI_FSL_REG_PHY1_TTA_MASK 0x0001ffff
|
||||
#define AHCI_FSL_REG_PHY1_SNM (1 << 17)
|
||||
#define AHCI_FSL_REG_PHY1_SNR (1 << 18)
|
||||
@ -85,73 +88,196 @@ __FBSDID("$FreeBSD$");
|
||||
#define AHCI_FSL_PHY2_CIBGN_SHIFT 16
|
||||
#define AHCI_FSL_PHY2_CINMP_SHIFT 24
|
||||
|
||||
|
||||
#define AHCI_FSL_PHY3_CWBGMN_SHIFT 0
|
||||
#define AHCI_FSL_PHY3_CWBGMX_SHIFT 8
|
||||
#define AHCI_FSL_PHY3_CWBGN_SHIFT 16
|
||||
#define AHCI_FSL_PHY3_CWNMP_SHIFT 24
|
||||
|
||||
/* Only in LS1021A */
|
||||
#define AHCI_FSL_PHY4_BMX_SHIFT 0
|
||||
#define AHCI_FSL_PHY4_BNM_SHIFT 8
|
||||
#define AHCI_FSL_PHY4_SFD_SHIFT 16
|
||||
#define AHCI_FSL_PHY4_PTST_SHIFT 24
|
||||
|
||||
/* Only in LS1021A */
|
||||
#define AHCI_FSL_PHY5_RIT_SHIFT 0
|
||||
#define AHCI_FSL_PHY5_RCT_SHIFT 20
|
||||
|
||||
#define AHCI_FSL_REG_PTC_RXWM_MASK 0x0000007f
|
||||
#define AHCI_FSL_REG_PTC_ENBD (1 << 8)
|
||||
#define AHCI_FSL_REG_PTC_ITM (1 << 9)
|
||||
|
||||
#define AHCI_FSL_REG_PHY1_CFG ((0x1fffe & AHCI_FSL_REG_PHY1_TTA_MASK) \
|
||||
| AHCI_FSL_REG_PHY1_SNM | AHCI_FSL_REG_PHY1_PSS \
|
||||
| AHCI_FSL_REG_PHY1_ESDF)
|
||||
#define AHCI_FSL_REG_PHY2_CFG ((0x1f << AHCI_FSL_PHY2_CIBGMN_SHIFT) \
|
||||
| (0x4d << AHCI_FSL_PHY2_CIBGMX_SHIFT) \
|
||||
| (0x18 << AHCI_FSL_PHY2_CIBGN_SHIFT) \
|
||||
| (0x28 << AHCI_FSL_PHY2_CINMP_SHIFT))
|
||||
#define AHCI_FSL_REG_PHY3_CFG ((0x09 << AHCI_FSL_PHY3_CWBGMN_SHIFT) \
|
||||
| (0x15 << AHCI_FSL_PHY3_CWBGMX_SHIFT) \
|
||||
| (0x08 << AHCI_FSL_PHY3_CWBGN_SHIFT) \
|
||||
| (0x0e << AHCI_FSL_PHY3_CWNMP_SHIFT))
|
||||
#define AHCI_FSL_REG_PHY1_CFG \
|
||||
((0x1fffe & AHCI_FSL_REG_PHY1_TTA_MASK) | \
|
||||
AHCI_FSL_REG_PHY1_SNM | AHCI_FSL_REG_PHY1_PSS | AHCI_FSL_REG_PHY1_ESDF)
|
||||
|
||||
#define AHCI_FSL_REG_PHY2_CFG \
|
||||
((0x1f << AHCI_FSL_PHY2_CIBGMN_SHIFT) | \
|
||||
(0x4d << AHCI_FSL_PHY2_CIBGMX_SHIFT) | \
|
||||
(0x18 << AHCI_FSL_PHY2_CIBGN_SHIFT) | \
|
||||
(0x28 << AHCI_FSL_PHY2_CINMP_SHIFT))
|
||||
|
||||
#define AHCI_FSL_REG_PHY2_CFG_LS1021A \
|
||||
((0x14 << AHCI_FSL_PHY2_CIBGMN_SHIFT) | \
|
||||
(0x34 << AHCI_FSL_PHY2_CIBGMX_SHIFT) | \
|
||||
(0x18 << AHCI_FSL_PHY2_CIBGN_SHIFT) | \
|
||||
(0x28 << AHCI_FSL_PHY2_CINMP_SHIFT))
|
||||
|
||||
#define AHCI_FSL_REG_PHY3_CFG \
|
||||
((0x09 << AHCI_FSL_PHY3_CWBGMN_SHIFT) | \
|
||||
(0x15 << AHCI_FSL_PHY3_CWBGMX_SHIFT) | \
|
||||
(0x08 << AHCI_FSL_PHY3_CWBGN_SHIFT) | \
|
||||
(0x0e << AHCI_FSL_PHY3_CWNMP_SHIFT))
|
||||
|
||||
#define AHCI_FSL_REG_PHY3_CFG_LS1021A \
|
||||
((0x06 << AHCI_FSL_PHY3_CWBGMN_SHIFT) | \
|
||||
(0x0e << AHCI_FSL_PHY3_CWBGMX_SHIFT) | \
|
||||
(0x08 << AHCI_FSL_PHY3_CWBGN_SHIFT) | \
|
||||
(0x0e << AHCI_FSL_PHY3_CWNMP_SHIFT))
|
||||
|
||||
#define AHCI_FSL_REG_PHY4_CFG_LS1021A \
|
||||
((0x0b << AHCI_FSL_PHY4_BMX_SHIFT) | \
|
||||
(0x08 << AHCI_FSL_PHY4_BNM_SHIFT) | \
|
||||
(0x4a << AHCI_FSL_PHY4_SFD_SHIFT) | \
|
||||
(0x06 << AHCI_FSL_PHY4_PTST_SHIFT))
|
||||
|
||||
#define AHCI_FSL_REG_PHY5_CFG_LS1021A \
|
||||
((0x86470 << AHCI_FSL_PHY5_RIT_SHIFT) | \
|
||||
(0x2aa << AHCI_FSL_PHY5_RCT_SHIFT))
|
||||
|
||||
/* Bit 27 enabled so value of reserved bits remains as in documentation. */
|
||||
#define AHCI_FSL_REG_PTC_CFG ((0x29 & AHCI_FSL_REG_PTC_RXWM_MASK) \
|
||||
| (1 << 27))
|
||||
#define AHCI_FSL_REG_PTC_CFG \
|
||||
((0x29 & AHCI_FSL_REG_PTC_RXWM_MASK) | (1 << 27))
|
||||
|
||||
#define AHCI_FSL_REG_AXICC_CFG 0x3fffffff
|
||||
|
||||
|
||||
#define AHCI_FSL_REG_ECC 0x0
|
||||
#define AHCI_FSL_REG_ECC_DIS 0x80000000
|
||||
#define AHCI_FSL_REG_ECC_LS1021A 0x00020000
|
||||
#define AHCI_FSL_REG_ECC_LS1043A 0x80000000
|
||||
#define AHCI_FSL_REG_ECC_LS1028A 0x40000000
|
||||
|
||||
struct ahci_fsl_fdt_soc_data;
|
||||
|
||||
#define QORIQ_AHCI_LS1021A 1
|
||||
#define QORIQ_AHCI_LS1028A 2
|
||||
#define QORIQ_AHCI_LS1043A 3
|
||||
#define QORIQ_AHCI_LS2080A 4
|
||||
#define QORIQ_AHCI_LS1046A 5
|
||||
#define QORIQ_AHCI_LS1088A 6
|
||||
#define QORIQ_AHCI_LS2088A 7
|
||||
#define QORIQ_AHCI_LX2160A 8
|
||||
|
||||
struct ahci_fsl_fdt_controller {
|
||||
struct ahci_controller ctlr; /* Must be the first field. */
|
||||
const struct ahci_fsl_fdt_soc_data *soc_data;
|
||||
int soc_type;
|
||||
struct resource *r_ecc;
|
||||
int r_ecc_rid;
|
||||
};
|
||||
|
||||
static void
|
||||
ahci_fsl_fdt_ls1046a_phy_init(struct ahci_fsl_fdt_controller *ctlr)
|
||||
{
|
||||
struct ahci_controller *ahci;
|
||||
uint32_t val;
|
||||
|
||||
ahci = &ctlr->ctlr;
|
||||
if (ctlr->r_ecc) {
|
||||
val = ATA_INL(ctlr->r_ecc, AHCI_FSL_REG_ECC) |
|
||||
AHCI_FSL_REG_ECC_DIS;
|
||||
ATA_OUTL(ctlr->r_ecc, AHCI_FSL_REG_ECC, val);
|
||||
}
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY1, AHCI_FSL_REG_PHY1_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY2, AHCI_FSL_REG_PHY2_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY3, AHCI_FSL_REG_PHY3_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PTC, AHCI_FSL_REG_PTC_CFG);
|
||||
}
|
||||
|
||||
struct ahci_fsl_fdt_soc_data {
|
||||
void (* phy_init)(struct ahci_fsl_fdt_controller *ctlr);
|
||||
};
|
||||
|
||||
static const struct ahci_fsl_fdt_soc_data ahci_fsl_fdt_ls1046a_soc_data = {
|
||||
.phy_init = ahci_fsl_fdt_ls1046a_phy_init,
|
||||
};
|
||||
|
||||
static const struct ofw_compat_data ahci_fsl_fdt_compat_data[] = {
|
||||
{"fsl,ls1046a-ahci", (uintptr_t)&ahci_fsl_fdt_ls1046a_soc_data},
|
||||
{"fsl,ls1021a-ahci", QORIQ_AHCI_LS1021A},
|
||||
{"fsl,ls1028a-ahci", QORIQ_AHCI_LS1028A},
|
||||
{"fsl,ls1043a-ahci", QORIQ_AHCI_LS1043A},
|
||||
{"fsl,ls2080a-ahci", QORIQ_AHCI_LS2080A},
|
||||
{"fsl,ls1046a-ahci", QORIQ_AHCI_LS1046A},
|
||||
{"fsl,ls1088a-ahci", QORIQ_AHCI_LS1088A},
|
||||
{"fsl,ls2088a-ahci", QORIQ_AHCI_LS2088A},
|
||||
{"fsl,lx2160a-ahci", QORIQ_AHCI_LX2160A},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static bool ecc_inited;
|
||||
|
||||
static int
|
||||
ahci_fsl_fdt_ecc_init(struct ahci_fsl_fdt_controller *ctrl)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
switch (ctrl->soc_type) {
|
||||
case QORIQ_AHCI_LS2080A:
|
||||
case QORIQ_AHCI_LS2088A:
|
||||
return (0);
|
||||
|
||||
case QORIQ_AHCI_LS1021A:
|
||||
if (!ecc_inited && ctrl->r_ecc == NULL)
|
||||
return (ENXIO);
|
||||
if (!ecc_inited)
|
||||
ATA_OUTL(ctrl->r_ecc, AHCI_FSL_REG_ECC,
|
||||
AHCI_FSL_REG_ECC_LS1021A);
|
||||
break;
|
||||
|
||||
case QORIQ_AHCI_LS1043A:
|
||||
case QORIQ_AHCI_LS1046A:
|
||||
if (!ecc_inited && ctrl->r_ecc == NULL)
|
||||
return (ENXIO);
|
||||
if (!ecc_inited) {
|
||||
val = ATA_INL(ctrl->r_ecc, AHCI_FSL_REG_ECC);
|
||||
val = AHCI_FSL_REG_ECC_LS1043A;
|
||||
ATA_OUTL(ctrl->r_ecc, AHCI_FSL_REG_ECC, val);
|
||||
}
|
||||
break;
|
||||
|
||||
case QORIQ_AHCI_LS1028A:
|
||||
case QORIQ_AHCI_LS1088A:
|
||||
case QORIQ_AHCI_LX2160A:
|
||||
if (!ecc_inited && ctrl->r_ecc == NULL)
|
||||
return (ENXIO);
|
||||
if (!ecc_inited) {
|
||||
|
||||
val = ATA_INL(ctrl->r_ecc, AHCI_FSL_REG_ECC);
|
||||
val |= AHCI_FSL_REG_ECC_LS1028A;
|
||||
ATA_OUTL(ctrl->r_ecc, AHCI_FSL_REG_ECC, val);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
panic("Unimplemented SOC type: %d", ctrl->soc_type);
|
||||
}
|
||||
|
||||
ecc_inited = true;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
ahci_fsl_fdt_phy_init(struct ahci_fsl_fdt_controller *ctrl)
|
||||
{
|
||||
struct ahci_controller *ahci;
|
||||
|
||||
ahci = &ctrl->ctlr;
|
||||
if (ctrl->soc_type == QORIQ_AHCI_LS1021A) {
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY1,
|
||||
AHCI_FSL_REG_PHY1_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY2,
|
||||
AHCI_FSL_REG_PHY2_CFG_LS1021A);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY3,
|
||||
AHCI_FSL_REG_PHY3_CFG_LS1021A);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY4,
|
||||
AHCI_FSL_REG_PHY4_CFG_LS1021A);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY5,
|
||||
AHCI_FSL_REG_PHY5_CFG_LS1021A);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PTC,
|
||||
AHCI_FSL_REG_PTC_CFG);
|
||||
|
||||
if (ctrl->ctlr.dma_coherent)
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_LS1021A_AXICC,
|
||||
AHCI_FSL_REG_AXICC_CFG);
|
||||
} else {
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY1,
|
||||
AHCI_FSL_REG_PHY1_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY2,
|
||||
AHCI_FSL_REG_PHY2_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PHY3,
|
||||
AHCI_FSL_REG_PHY3_CFG);
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_PTC,
|
||||
AHCI_FSL_REG_PTC_CFG);
|
||||
|
||||
if (ctrl->ctlr.dma_coherent)
|
||||
ATA_OUTL(ahci->r_mem, AHCI_FSL_REG_AXICC,
|
||||
AHCI_FSL_REG_AXICC_CFG);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ahci_fsl_fdt_probe(device_t dev)
|
||||
{
|
||||
@ -170,21 +296,21 @@ ahci_fsl_fdt_attach(device_t dev)
|
||||
{
|
||||
struct ahci_fsl_fdt_controller *ctlr;
|
||||
struct ahci_controller *ahci;
|
||||
uintptr_t ocd_data;
|
||||
phandle_t node;
|
||||
clk_t clock;
|
||||
int ret;
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
ctlr = device_get_softc(dev);
|
||||
ocd_data = ofw_bus_search_compatible(dev,
|
||||
ahci_fsl_fdt_compat_data)->ocd_data;
|
||||
ctlr->soc_data = (struct ahci_fsl_fdt_soc_data *)ocd_data;
|
||||
ctlr->soc_type =
|
||||
ofw_bus_search_compatible(dev, ahci_fsl_fdt_compat_data)->ocd_data;
|
||||
ahci = &ctlr->ctlr;
|
||||
ahci->dev = dev;
|
||||
ahci->r_rid = 0;
|
||||
ahci->quirks = AHCI_Q_NOPMP;
|
||||
|
||||
ahci->dma_coherent = OF_hasprop(node, "dma-coherent");
|
||||
|
||||
ret = clk_get_by_ofw_index(dev, node, 0, &clock);
|
||||
if (ret != 0) {
|
||||
device_printf(dev, "No clock found.\n");
|
||||
@ -199,8 +325,8 @@ ahci_fsl_fdt_attach(device_t dev)
|
||||
|
||||
if (OF_hasprop(node, "reg-names") && ofw_bus_find_string_index(node,
|
||||
"reg-names", "ahci", &ahci->r_rid)) {
|
||||
device_printf(dev,
|
||||
"Could not locate \"ahci\" string in the \"reg-names\" property");
|
||||
device_printf(dev, "Could not locate 'ahci' string in the "
|
||||
"'reg-names' property");
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
@ -212,21 +338,32 @@ ahci_fsl_fdt_attach(device_t dev)
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
if (!ofw_bus_find_string_index(node, "reg-names", "sata-ecc",
|
||||
&ctlr->r_ecc_rid)) {
|
||||
ret = ofw_bus_find_string_index(node, "reg-names", "sata-ecc",
|
||||
&ctlr->r_ecc_rid);
|
||||
if (ret == 0) {
|
||||
ctlr->r_ecc = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
||||
&ctlr->r_ecc_rid, RF_ACTIVE);
|
||||
&ctlr->r_ecc_rid, RF_ACTIVE| RF_SHAREABLE);
|
||||
if (!ctlr->r_ecc) {
|
||||
device_printf(dev,
|
||||
"Could not allocate resources for controller\n");
|
||||
ret = ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
} else if (ret != ENOENT) {
|
||||
device_printf(dev, "Could not locate 'sata-ecc' string in "
|
||||
"the 'reg-names' property");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ret = ahci_fsl_fdt_ecc_init(ctlr);
|
||||
if (ret != 0) {
|
||||
device_printf(dev, "Could not initialize 'ecc' registers");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/* Setup controller defaults. */
|
||||
ahci->numirqs = 1;
|
||||
ctlr->soc_data->phy_init(ctlr);
|
||||
ahci_fsl_fdt_phy_init(ctlr);
|
||||
|
||||
/* Reset controller. */
|
||||
ret = ahci_ctlr_reset(dev);
|
||||
|
162
sys/dev/iicbus/mux/pca9547.c
Normal file
162
sys/dev/iicbus/mux/pca9547.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/ofw/openfirm.h>
|
||||
|
||||
#include "iicbus_if.h"
|
||||
#include "iicmux_if.h"
|
||||
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{"nxp,pca9547", 1},
|
||||
{NULL, 0}
|
||||
};
|
||||
IICBUS_FDT_PNP_INFO(compat_data);
|
||||
|
||||
#include <dev/iicbus/mux/iicmux.h>
|
||||
|
||||
struct pca9547_softc {
|
||||
struct iicmux_softc mux;
|
||||
device_t dev;
|
||||
bool idle_disconnect;
|
||||
};
|
||||
|
||||
static int
|
||||
pca9547_bus_select(device_t dev, int busidx, struct iic_reqbus_data *rd)
|
||||
{
|
||||
struct pca9547_softc *sc = device_get_softc(dev);
|
||||
uint8_t busbits;
|
||||
|
||||
/*
|
||||
* The iicmux caller ensures busidx is between 0 and the number of buses
|
||||
* we passed to iicmux_init_softc(), no need for validation here. If
|
||||
* the fdt data has the idle_disconnect property we idle the bus by
|
||||
* selecting no downstream buses, otherwise we just leave the current
|
||||
* bus active. The upper bits of control register 3 activate the
|
||||
* downstream buses; bit 7 is the first bus, bit 6 the second, etc.
|
||||
*/
|
||||
if (busidx == IICMUX_SELECT_IDLE) {
|
||||
if (sc->idle_disconnect)
|
||||
busbits = 0;
|
||||
else
|
||||
return (0);
|
||||
} else {
|
||||
busbits = 0x8 | (busidx & 0x7);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to add the IIC_RECURSIVE flag because the iicmux core has
|
||||
* already reserved the bus for us, and iicdev_writeto() is going to try
|
||||
* to reserve it again, which is allowed with the recursive flag.
|
||||
*/
|
||||
|
||||
return (iicdev_writeto(dev, busbits, NULL, 0,
|
||||
rd->flags | IIC_RECURSIVE));
|
||||
}
|
||||
|
||||
static int
|
||||
pca9547_probe(device_t dev)
|
||||
{
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "PCA9547 IIC bus multiplexor");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
pca9547_attach(device_t dev)
|
||||
{
|
||||
struct pca9547_softc *sc;
|
||||
phandle_t node;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc ->dev = dev;
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
sc->idle_disconnect = OF_hasprop(node, "i2c-mux-idle-disconnect");
|
||||
|
||||
rv = iicmux_attach(sc->dev, device_get_parent(dev), 8);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
rv = bus_generic_attach(dev);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static int
|
||||
pca9547_detach(device_t dev)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = iicmux_detach(dev);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t pca9547_methods[] = {
|
||||
/* device methods */
|
||||
DEVMETHOD(device_probe, pca9547_probe),
|
||||
DEVMETHOD(device_attach, pca9547_attach),
|
||||
DEVMETHOD(device_detach, pca9547_detach),
|
||||
|
||||
/* iicmux methods */
|
||||
DEVMETHOD(iicmux_bus_select, pca9547_bus_select),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static devclass_t pca9547_devclass;
|
||||
DEFINE_CLASS_1(iicmux, pca9547_driver, pca9547_methods,
|
||||
sizeof(struct pca9547_softc), iicmux_driver);
|
||||
DRIVER_MODULE(pca_iicmux, iicbus, pca9547_driver, pca9547_devclass, 0, 0);
|
||||
DRIVER_MODULE(iicbus, iicmux, iicbus_driver, iicbus_devclass, 0, 0);
|
||||
DRIVER_MODULE(ofw_iicbus, iicmux, ofw_iicbus_driver, ofw_iicbus_devclass,
|
||||
0, 0);
|
||||
MODULE_DEPEND(pca9547, iicmux, 1, 1, 1);
|
||||
MODULE_DEPEND(pca9547, iicbus, 1, 1, 1);
|
Loading…
x
Reference in New Issue
Block a user