Author: mmel Date: Sat Dec 5 12:08:37 2020 New Revision: 368369 URL: https://svnweb.freebsd.org/changeset/base/368369
Log: Add basic support for Freescale LX2160A SoC. All peripherals but the network processor are supported. Added: head/sys/arm64/qoriq/clk/lx2160a_clkgen.c (contents, props changed) head/sys/arm64/qoriq/qoriq_dw_pci.c (contents, props changed) head/sys/arm64/qoriq/qoriq_therm.c (contents, props changed) head/sys/arm64/qoriq/qoriq_therm_if.m (contents, props changed) head/sys/dev/iicbus/mux/pca9547.c (contents, props changed) Modified: head/sys/arm/freescale/vybrid/vf_i2c.c head/sys/conf/files head/sys/conf/files.arm64 head/sys/dev/ahci/ahci_fsl_fdt.c Modified: head/sys/arm/freescale/vybrid/vf_i2c.c ============================================================================== --- head/sys/arm/freescale/vybrid/vf_i2c.c Sat Dec 5 11:18:37 2020 (r368368) +++ head/sys/arm/freescale/vybrid/vf_i2c.c Sat Dec 5 12:08:37 2020 (r368369) @@ -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,7 +308,15 @@ 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 tim 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,13 +399,19 @@ 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 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); Added: head/sys/arm64/qoriq/clk/lx2160a_clkgen.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/qoriq/clk/lx2160a_clkgen.c Sat Dec 5 12:08:37 2020 (r368369) @@ -0,0 +1,211 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Michal Meloun <m...@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); Added: head/sys/arm64/qoriq/qoriq_dw_pci.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/qoriq/qoriq_dw_pci.c Sat Dec 5 12:08:37 2020 (r368369) @@ -0,0 +1,256 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Michal Meloun <m...@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); Added: head/sys/arm64/qoriq/qoriq_therm.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/qoriq/qoriq_therm.c Sat Dec 5 12:08:37 2020 (r368369) @@ -0,0 +1,406 @@ +/*- + * + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Michal Meloun <m...@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); Added: head/sys/arm64/qoriq/qoriq_therm_if.m ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/qoriq/qoriq_therm_if.m Sat Dec 5 12:08:37 2020 (r368369) @@ -0,0 +1,44 @@ +#- +# +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright 2020 Michal Meloun <m...@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 *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"