Add basic support for Freescale LX2160A SoC.

All peripherals but the network processor are supported.
This commit is contained in:
Michal Meloun 2020-12-05 12:08:37 +00:00
parent 2f3e7fb2cc
commit 6844eecfed
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=368369
9 changed files with 1306 additions and 65 deletions

View File

@ -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);

View 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);

View 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);

View 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);

View 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;
};

View File

@ -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

View File

@ -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

View File

@ -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);

View 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);