Add i.MX 8M Quad support

- Add CCM driver and clocks implementations for i.MX 8M
- Add GPC driver for iMX8
- Add clock tree for i.MX 8M Quad
- Add clocks support and new compat strings (where required) for existing i.MX 6 UART, I2C, and GPIO drivers
- Enable aarch64-compatible drivers form i.MX 6 in arm64 GENERIC kernel config
- Add dtb/imx8 kernel module with DTBs for Nitrogen8M and iMX8MQ EVK

With this patch both Nitrogen8M and iMX8MQ EVK boot with NFS root up to multiuser login prompt

Reviewed by:	manu
Differential Revision:	https://reviews.freebsd.org/D25274
This commit is contained in:
Oleksandr Tymoshenko 2020-07-01 00:33:16 +00:00
parent 39ca7ca568
commit 94bc2117b4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=362817
24 changed files with 2464 additions and 9 deletions

View File

@ -57,6 +57,14 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#if defined(EXT_RESOURCES) && defined(__aarch64__)
#define IMX_ENABLE_CLOCKS
#endif
#ifdef IMX_ENABLE_CLOCKS
#include <dev/extres/clk/clk.h>
#endif
#include "gpio_if.h"
#ifdef INTRNG
@ -119,13 +127,17 @@ struct imx51_gpio_softc {
#ifdef INTRNG
struct gpio_irqsrc gpio_pic_irqsrc[NGPIO];
#endif
#ifdef IMX_ENABLE_CLOCKS
clk_t clk;
#endif
};
static struct ofw_compat_data compat_data[] = {
{"fsl,imx6q-gpio", 1},
{"fsl,imx53-gpio", 1},
{"fsl,imx51-gpio", 1},
{NULL, 0}
{"fsl,imx8mq-gpio", 1},
{"fsl,imx6q-gpio", 1},
{"fsl,imx53-gpio", 1},
{"fsl,imx51-gpio", 1},
{NULL, 0}
};
static struct resource_spec imx_gpio_spec[] = {
@ -788,6 +800,9 @@ imx51_gpio_attach(device_t dev)
{
struct imx51_gpio_softc *sc;
int i, irq, unit;
#ifdef IMX_ENABLE_CLOCKS
int err;
#endif
sc = device_get_softc(dev);
sc->dev = dev;
@ -795,6 +810,19 @@ imx51_gpio_attach(device_t dev)
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN);
#ifdef IMX_ENABLE_CLOCKS
if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk) != 0) {
device_printf(dev, "could not get clock");
return (ENOENT);
}
err = clk_enable(sc->clk);
if (err != 0) {
device_printf(sc->dev, "could not enable ipg clock\n");
return (err);
}
#endif
if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) {
device_printf(dev, "could not allocate resources\n");
bus_release_resources(dev, imx_gpio_spec, sc->sc_res);
@ -850,9 +878,20 @@ imx51_gpio_detach(device_t dev)
{
int irq;
struct imx51_gpio_softc *sc;
#ifdef IMX_ENABLE_CLOCKS
int error;
#endif
sc = device_get_softc(dev);
#ifdef IMX_ENABLE_CLOCKS
error = clk_disable(sc->clk);
if (error != 0) {
device_printf(sc->dev, "could not disable ipg clock\n");
return (error);
}
#endif
gpiobus_detach_bus(dev);
for (irq = 0; irq < NUM_IRQRES; irq++) {
if (sc->gpio_ih[irq])

View File

@ -73,6 +73,14 @@ __FBSDID("$FreeBSD$");
#include <dev/fdt/fdt_pinctrl.h>
#include <dev/gpio/gpiobusvar.h>
#if defined(EXT_RESOURCES) && defined(__aarch64__)
#define IMX_ENABLE_CLOCKS
#endif
#ifdef IMX_ENABLE_CLOCKS
#include <dev/extres/clk/clk.h>
#endif
#define I2C_ADDR_REG 0x00 /* I2C slave address register */
#define I2C_FDR_REG 0x04 /* I2C frequency divider register */
#define I2C_CONTROL_REG 0x08 /* I2C control register */
@ -125,6 +133,7 @@ static struct clkdiv clkdiv_table[] = {
};
static struct ofw_compat_data compat_data[] = {
{"fsl,imx21-i2c", 1},
{"fsl,imx6q-i2c", 1},
{"fsl,imx-i2c", 1},
{NULL, 0}
@ -141,6 +150,9 @@ struct i2c_softc {
gpio_pin_t rb_sdapin;
u_int debug;
u_int slave;
#ifdef IMX_ENABLE_CLOCKS
clk_t ipgclk;
#endif
};
#define DEVICE_DEBUGF(sc, lvl, fmt, args...) \
@ -385,6 +397,19 @@ i2c_attach(device_t dev)
sc->dev = dev;
sc->rid = 0;
#ifdef IMX_ENABLE_CLOCKS
if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->ipgclk) != 0) {
device_printf(dev, "could not get ipg clock");
return (ENOENT);
}
err = clk_enable(sc->ipgclk);
if (err != 0) {
device_printf(sc->dev, "could not enable ipg clock\n");
return (err);
}
#endif
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
RF_ACTIVE);
if (sc->res == NULL) {
@ -459,6 +484,14 @@ i2c_detach(device_t dev)
sc = device_get_softc(dev);
#ifdef IMX_ENABLE_CLOCKS
error = clk_disable(sc->ipgclk);
if (error != 0) {
device_printf(sc->dev, "could not disable ipg clock\n");
return (error);
}
#endif
if ((error = bus_generic_detach(sc->dev)) != 0) {
device_printf(sc->dev, "cannot detach child devices\n");
return (error);
@ -571,6 +604,10 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr)
{
struct i2c_softc *sc;
u_int busfreq, div, i, ipgfreq;
#ifdef IMX_ENABLE_CLOCKS
int err;
uint64_t freq;
#endif
sc = device_get_softc(dev);
@ -580,7 +617,16 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr)
* Look up the divisor that gives the nearest speed that doesn't exceed
* the configured value for the bus.
*/
#ifdef IMX_ENABLE_CLOCKS
err = clk_get_freq(sc->ipgclk, &freq);
if (err != 0) {
device_printf(sc->dev, "cannot get frequency\n");
return (err);
}
ipgfreq = (int32_t)freq;
#else
ipgfreq = imx_ccm_ipg_hz();
#endif
busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
div = howmany(ipgfreq, busfreq);
for (i = 0; i < nitems(clkdiv_table); i++) {

View File

@ -76,6 +76,7 @@ struct iomux_softc {
static struct iomux_softc *iomux_sc;
static struct ofw_compat_data compat_data[] = {
{"fsl,imx8mq-iomuxc", true},
{"fsl,imx6dl-iomuxc", true},
{"fsl,imx6q-iomuxc", true},
{"fsl,imx6sl-iomuxc", true},

View File

@ -113,6 +113,7 @@ options SOC_ALLWINNER_A64
options SOC_ALLWINNER_H5
options SOC_ALLWINNER_H6
options SOC_CAVM_THUNDERX
options SOC_FREESCALE_IMX8
options SOC_HISI_HI6220
options SOC_INTEL_STRATIX10
options SOC_BRCM_BCM2837
@ -172,6 +173,7 @@ device al_eth # Annapurna Alpine Ethernet NIC
device dwc_rk # Rockchip Designware
device dwc_socfpga # Altera SOCFPGA Ethernet MAC
device genet # Broadcom on RPi4
device ffec # iMX FFEC
# Etherswitch devices
device etherswitch # Enable etherswitch support
@ -205,6 +207,7 @@ device rk_emmcphy
# Serial (COM) ports
device uart # Generic UART driver
device uart_imx # iMX8 UART
device uart_msm # Qualcomm MSM UART driver
device uart_mu # RPI3 aux port
device uart_mvebu # Armada 3700 UART driver
@ -265,6 +268,7 @@ device rk_i2c # RockChip I2C controller
device syr827 # Silergy SYR827 PMIC
device sy8106a # SY8106A Buck Regulator
device vf_i2c # Freescale Vybrid I2C controller
device fsliic # Freescale iMX I2C controller
# Clock and reset controllers
device aw_ccu # Allwinner clock controller
@ -352,4 +356,4 @@ options FDT
device acpi
# DTBs
makeoptions MODULES_EXTRA="dtb/allwinner dtb/mv dtb/rockchip dtb/rpi"
makeoptions MODULES_EXTRA="dtb/allwinner dtb/imx8 dtb/mv dtb/rockchip dtb/rpi"

View File

@ -0,0 +1,309 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Emmanuel Vadot <manu@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 ``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 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm64/freescale/imx/clk/imx_clk_composite.h>
#include "clkdev_if.h"
#define TARGET_ROOT_ENABLE (1 << 28)
#define TARGET_ROOT_MUX(n) ((n) << 24)
#define TARGET_ROOT_MUX_MASK (7 << 24)
#define TARGET_ROOT_MUX_SHIFT 24
#define TARGET_ROOT_PRE_PODF(n) ((((n) - 1) & 0x7) << 16)
#define TARGET_ROOT_PRE_PODF_MASK (0x7 << 16)
#define TARGET_ROOT_PRE_PODF_SHIFT 16
#define TARGET_ROOT_PRE_PODF_MAX 7
#define TARGET_ROOT_POST_PODF(n) ((((n) - 1) & 0x3f) << 0)
#define TARGET_ROOT_POST_PODF_MASK (0x3f << 0)
#define TARGET_ROOT_POST_PODF_SHIFT 0
#define TARGET_ROOT_POST_PODF_MAX 0x3f
struct imx_clk_composite_sc {
uint32_t offset;
uint32_t flags;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
#define IMX_CLK_COMPOSITE_MASK_SHIFT 16
#if 0
#define dprintf(format, arg...) \
printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
#else
#define dprintf(format, arg...)
#endif
static int
imx_clk_composite_init(struct clknode *clk, device_t dev)
{
struct imx_clk_composite_sc *sc;
uint32_t val, idx;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
DEVICE_UNLOCK(clk);
idx = (val & TARGET_ROOT_MUX_MASK) >> TARGET_ROOT_MUX_SHIFT;
clknode_init_parent_idx(clk, idx);
return (0);
}
static int
imx_clk_composite_set_gate(struct clknode *clk, bool enable)
{
struct imx_clk_composite_sc *sc;
uint32_t val = 0;
sc = clknode_get_softc(clk);
dprintf("%sabling gate\n", enable ? "En" : "Dis");
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
if (enable)
val |= TARGET_ROOT_ENABLE;
else
val &= ~(TARGET_ROOT_ENABLE);
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
return (0);
}
static int
imx_clk_composite_set_mux(struct clknode *clk, int index)
{
struct imx_clk_composite_sc *sc;
uint32_t val = 0;
sc = clknode_get_softc(clk);
dprintf("Set mux to %d\n", index);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
val &= ~(TARGET_ROOT_MUX_MASK);
val |= TARGET_ROOT_MUX(index);
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
return (0);
}
static int
imx_clk_composite_recalc(struct clknode *clk, uint64_t *freq)
{
struct imx_clk_composite_sc *sc;
uint32_t reg, pre_div, post_div;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
pre_div = ((reg & TARGET_ROOT_PRE_PODF_MASK)
>> TARGET_ROOT_PRE_PODF_SHIFT) + 1;
post_div = ((reg & TARGET_ROOT_POST_PODF_MASK)
>> TARGET_ROOT_POST_PODF_SHIFT) + 1;
dprintf("parent_freq=%ju, div=%u\n", *freq, div);
*freq = *freq / pre_div / post_div;
dprintf("Final freq=%ju\n", *freq);
return (0);
}
static int
imx_clk_composite_find_best(uint64_t fparent, uint64_t ftarget,
uint32_t *pre_div, uint32_t *post_div, int flags)
{
uint32_t prediv, postdiv, best_prediv, best_postdiv;
int64_t diff, best_diff;
uint64_t cur;
best_diff = INT64_MAX;
for (prediv = 1; prediv <= TARGET_ROOT_PRE_PODF_MAX + 1; prediv++) {
for (postdiv = 1; postdiv <= TARGET_ROOT_POST_PODF_MAX + 1; postdiv++) {
cur= fparent / prediv / postdiv;
diff = (int64_t)ftarget - (int64_t)cur;
if (flags & CLK_SET_ROUND_DOWN) {
if (diff >= 0 && diff < best_diff) {
best_diff = diff;
best_prediv = prediv;
best_postdiv = postdiv;
}
}
else if (flags & CLK_SET_ROUND_UP) {
if (diff <= 0 && abs(diff) < best_diff) {
best_diff = diff;
best_prediv = prediv;
best_postdiv = postdiv;
}
}
else {
if (abs(diff) < best_diff) {
best_diff = abs(diff);
best_prediv = prediv;
best_postdiv = postdiv;
}
}
}
}
if (best_diff == INT64_MAX)
return (ERANGE);
*pre_div = best_prediv;
*post_div = best_postdiv;
return (0);
}
static int
imx_clk_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
int flags, int *stop)
{
struct imx_clk_composite_sc *sc;
struct clknode *p_clk;
const char **p_names;
int p_idx, best_parent;
int64_t best_diff, diff;
int32_t best_pre_div, best_post_div, pre_div, post_div;
uint64_t cur, best;
uint32_t val;
sc = clknode_get_softc(clk);
dprintf("Finding best parent/div for target freq of %ju\n", *fout);
p_names = clknode_get_parent_names(clk);
best_diff = 0;
for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {
p_clk = clknode_find_by_name(p_names[p_idx]);
clknode_get_freq(p_clk, &fparent);
dprintf("Testing with parent %s (%d) at freq %ju\n",
clknode_get_name(p_clk), p_idx, fparent);
if (!imx_clk_composite_find_best(fparent, *fout, &pre_div, &post_div, sc->flags))
continue;
cur = fparent / pre_div / post_div;
diff = abs((int64_t)*fout - (int64_t)cur);
if (diff < best_diff) {
best = cur;
best_diff = diff;
best_pre_div = pre_div;
best_post_div = pre_div;
best_parent = p_idx;
dprintf("Best parent so far %s (%d) with best freq at "
"%ju\n", clknode_get_name(p_clk), p_idx, best);
}
}
*stop = 1;
if (best_diff == INT64_MAX)
return (ERANGE);
if ((flags & CLK_SET_DRYRUN) != 0) {
*fout = best;
return (0);
}
p_idx = clknode_get_parent_idx(clk);
if (p_idx != best_parent) {
dprintf("Switching parent index from %d to %d\n", p_idx,
best_parent);
clknode_set_parent_by_idx(clk, best_parent);
}
dprintf("Setting dividers to pre=%d, post=%d\n", best_pre_div, best_post_div);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
val &= ~(TARGET_ROOT_PRE_PODF_MASK | TARGET_ROOT_POST_PODF_MASK);
val |= TARGET_ROOT_PRE_PODF(pre_div);
val |= TARGET_ROOT_POST_PODF(post_div);
DEVICE_UNLOCK(clk);
*fout = best;
return (0);
}
static clknode_method_t imx_clk_composite_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, imx_clk_composite_init),
CLKNODEMETHOD(clknode_set_gate, imx_clk_composite_set_gate),
CLKNODEMETHOD(clknode_set_mux, imx_clk_composite_set_mux),
CLKNODEMETHOD(clknode_recalc_freq, imx_clk_composite_recalc),
CLKNODEMETHOD(clknode_set_freq, imx_clk_composite_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(imx_clk_composite_clknode, imx_clk_composite_clknode_class,
imx_clk_composite_clknode_methods, sizeof(struct imx_clk_composite_sc),
clknode_class);
int
imx_clk_composite_register(struct clkdom *clkdom,
struct imx_clk_composite_def *clkdef)
{
struct clknode *clk;
struct imx_clk_composite_sc *sc;
clk = clknode_create(clkdom, &imx_clk_composite_clknode_class,
&clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->flags = clkdef->flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,45 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2018 Emmanuel Vadot <manu@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$
*/
#ifndef _IMX_CLK_COMPOSITE_H_
#define _IMX_CLK_COMPOSITE_H_
#include <dev/extres/clk/clk.h>
struct imx_clk_composite_def {
struct clknode_init_def clkdef;
uint32_t offset;
uint32_t flags;
};
int imx_clk_composite_register(struct clkdom *clkdom,
struct imx_clk_composite_def *clkdef);
#endif /* _IMX_CLK_COMPOSITE_H_ */

View File

@ -0,0 +1,177 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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 ``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 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm64/freescale/imx/clk/imx_clk_frac_pll.h>
#include "clkdev_if.h"
struct imx_clk_frac_pll_sc {
uint32_t offset;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
#define CFG0 0
#define CFG0_PLL_LOCK (1 << 31)
#define CFG0_PD (1 << 19)
#define CFG0_BYPASS (1 << 14)
#define CFG0_NEWDIV_VAL (1 << 12)
#define CFG0_NEWDIV_ACK (1 << 11)
#define CFG0_OUTPUT_DIV_MASK (0x1f << 0)
#define CFG0_OUTPUT_DIV_SHIFT 0
#define CFG1 4
#define CFG1_FRAC_DIV_MASK (0xffffff << 7)
#define CFG1_FRAC_DIV_SHIFT 7
#define CFG1_INT_DIV_MASK (0x7f << 0)
#define CFG1_INT_DIV_SHIFT 0
#if 0
#define dprintf(format, arg...) \
printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
#else
#define dprintf(format, arg...)
#endif
static int
imx_clk_frac_pll_init(struct clknode *clk, device_t dev)
{
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
imx_clk_frac_pll_set_gate(struct clknode *clk, bool enable)
{
struct imx_clk_frac_pll_sc *sc;
uint32_t cfg0;
int timeout;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset + CFG0, &cfg0);
if (enable)
cfg0 &= ~(CFG0_PD);
else
cfg0 |= CFG0_PD;
WRITE4(clk, sc->offset + CFG0, cfg0);
/* Wait for PLL to lock */
if (enable && ((cfg0 & CFG0_BYPASS) == 0)) {
for (timeout = 1000; timeout; timeout--) {
READ4(clk, sc->offset + CFG0, &cfg0);
if (cfg0 & CFG0_PLL_LOCK)
break;
DELAY(1);
}
}
DEVICE_UNLOCK(clk);
return (0);
}
static int
imx_clk_frac_pll_recalc(struct clknode *clk, uint64_t *freq)
{
struct imx_clk_frac_pll_sc *sc;
uint32_t cfg0, cfg1;
uint64_t div, divfi, divff, divf_val;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset + CFG0, &cfg0);
READ4(clk, sc->offset + CFG1, &cfg1);
DEVICE_UNLOCK(clk);
div = (cfg0 & CFG0_OUTPUT_DIV_MASK) >> CFG0_OUTPUT_DIV_SHIFT;
div = (div + 1) * 2;
divff = (cfg1 & CFG1_FRAC_DIV_MASK) >> CFG1_FRAC_DIV_SHIFT;
divfi = (cfg1 & CFG1_INT_DIV_MASK) >> CFG1_INT_DIV_SHIFT;
/* PLL is bypassed */
if (cfg0 & CFG0_BYPASS)
return (0);
divf_val = 1 + divfi + (divff/0x1000000);
*freq = *freq * 8 * divf_val / div;
return (0);
}
static clknode_method_t imx_clk_frac_pll_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, imx_clk_frac_pll_init),
CLKNODEMETHOD(clknode_set_gate, imx_clk_frac_pll_set_gate),
CLKNODEMETHOD(clknode_recalc_freq, imx_clk_frac_pll_recalc),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(imx_clk_frac_pll_clknode, imx_clk_frac_pll_clknode_class,
imx_clk_frac_pll_clknode_methods, sizeof(struct imx_clk_frac_pll_sc),
clknode_class);
int
imx_clk_frac_pll_register(struct clkdom *clkdom,
struct imx_clk_frac_pll_def *clkdef)
{
struct clknode *clk;
struct imx_clk_frac_pll_sc *sc;
clk = clknode_create(clkdom, &imx_clk_frac_pll_clknode_class,
&clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,42 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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$
*/
#ifndef _IMX_CLK_FRAC_PLL_H_
#define _IMX_CLK_FRAC_PLL_H_
#include <dev/extres/clk/clk.h>
struct imx_clk_frac_pll_def {
struct clknode_init_def clkdef;
uint32_t offset;
};
int imx_clk_frac_pll_register(struct clkdom *clkdom, struct imx_clk_frac_pll_def *clkdef);
#endif /* _IMX_CLK_FRAC_PLL_H_ */

View File

@ -0,0 +1,117 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm64/freescale/imx/clk/imx_clk_gate.h>
#include "clkdev_if.h"
#define WR4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define RD4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define MD4(_clk, off, clr, set ) \
CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
static int imx_clk_gate_init(struct clknode *clk, device_t dev);
static int imx_clk_gate_set_gate(struct clknode *clk, bool enable);
struct imx_clk_gate_sc {
uint32_t offset;
uint32_t shift;
uint32_t mask;
int gate_flags;
};
static clknode_method_t imx_clk_gate_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, imx_clk_gate_init),
CLKNODEMETHOD(clknode_set_gate, imx_clk_gate_set_gate),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(imx_clk_gate, imx_clk_gate_class, imx_clk_gate_methods,
sizeof(struct imx_clk_gate_sc), clknode_class);
static int
imx_clk_gate_init(struct clknode *clk, device_t dev)
{
clknode_init_parent_idx(clk, 0);
return(0);
}
static int
imx_clk_gate_set_gate(struct clknode *clk, bool enable)
{
uint32_t reg;
struct imx_clk_gate_sc *sc;
int rv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
rv = MD4(clk, sc->offset, sc->mask << sc->shift,
(enable ? sc->mask : 0) << sc->shift);
if (rv != 0) {
DEVICE_UNLOCK(clk);
return (rv);
}
RD4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
return(0);
}
int
imx_clk_gate_register(struct clkdom *clkdom, struct imx_clk_gate_def *clkdef)
{
struct clknode *clk;
struct imx_clk_gate_sc *sc;
clk = clknode_create(clkdom, &imx_clk_gate_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->shift = clkdef->shift;
sc->mask = clkdef->mask;
sc->gate_flags = clkdef->gate_flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,45 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2018 Emmanuel Vadot <manu@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$
*/
#ifndef _IMX_CLK_GATE_H_
#define _IMX_CLK_GATE_H_
#include <dev/extres/clk/clk.h>
struct imx_clk_gate_def {
struct clknode_init_def clkdef;
uint32_t offset;
uint32_t shift;
uint32_t mask;
int gate_flags;
};
int imx_clk_gate_register(struct clkdom *clkdom, struct imx_clk_gate_def *clkdef);
#endif /* _IMX_CLK_GATE_H_ */

View File

@ -0,0 +1,138 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm64/freescale/imx/clk/imx_clk_mux.h>
#include "clkdev_if.h"
#define WR4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define RD4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define MD4(_clk, off, clr, set ) \
CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
static int imx_clk_mux_init(struct clknode *clk, device_t dev);
static int imx_clk_mux_set_mux(struct clknode *clk, int idx);
struct imx_clk_mux_sc {
uint32_t offset;
uint32_t shift;
uint32_t mask;
int mux_flags;
};
static clknode_method_t imx_clk_mux_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, imx_clk_mux_init),
CLKNODEMETHOD(clknode_set_mux, imx_clk_mux_set_mux),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(imx_clk_mux, imx_clk_mux_class, imx_clk_mux_methods,
sizeof(struct imx_clk_mux_sc), clknode_class);
static int
imx_clk_mux_init(struct clknode *clk, device_t dev)
{
uint32_t reg;
struct imx_clk_mux_sc *sc;
int rv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
rv = RD4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
if (rv != 0) {
return (rv);
}
reg = (reg >> sc->shift) & sc->mask;
clknode_init_parent_idx(clk, reg);
return(0);
}
static int
imx_clk_mux_set_mux(struct clknode *clk, int idx)
{
uint32_t reg;
struct imx_clk_mux_sc *sc;
int rv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
rv = MD4(clk, sc->offset, sc->mask << sc->shift,
((idx & sc->mask) << sc->shift));
if (rv != 0) {
DEVICE_UNLOCK(clk);
return (rv);
}
RD4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
return(0);
}
int
imx_clk_mux_register(struct clkdom *clkdom, struct imx_clk_mux_def *clkdef)
{
struct clknode *clk;
struct imx_clk_mux_sc *sc;
clk = clknode_create(clkdom, &imx_clk_mux_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->shift = clkdef->shift;
sc->mask = (1 << clkdef->width) - 1;
sc->mux_flags = clkdef->mux_flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,45 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
* All rights reserved.
*
* 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$
*/
#ifndef _IMX_CLK_MUX_H_
#define _IMX_CLK_MUX_H_
#include <dev/extres/clk/clk.h>
struct imx_clk_mux_def {
struct clknode_init_def clkdef;
uint32_t offset;
uint32_t shift;
uint32_t width;
int mux_flags;
};
int imx_clk_mux_register(struct clkdom *clkdom, struct imx_clk_mux_def *clkdef);
#endif /* _IMX_CLK_MUX_H_ */

View File

@ -0,0 +1,195 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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 ``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 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm64/freescale/imx/clk/imx_clk_sscg_pll.h>
#include "clkdev_if.h"
struct imx_clk_sscg_pll_sc {
uint32_t offset;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
#define CFG0 0x00
#define CFG0_PLL_LOCK (1 << 31)
#define CFG0_PD (1 << 7)
#define CFG0_BYPASS2 (1 << 5)
#define CFG0_BYPASS1 (1 << 4)
#define CFG1 0x04
#define CFG2 0x08
#define CFG2_DIVR1_MASK (7 << 25)
#define CFG2_DIVR1_SHIFT 25
#define CFG2_DIVR2_MASK (0x3f << 19)
#define CFG2_DIVR2_SHIFT 19
#define CFG2_DIVF1_MASK (0x3f << 13)
#define CFG2_DIVF1_SHIFT 13
#define CFG2_DIVF2_MASK (0x3f << 7)
#define CFG2_DIVF2_SHIFT 7
#define CFG2_DIV_MASK (0x3f << 1)
#define CFG2_DIV_SHIFT 1
#if 0
#define dprintf(format, arg...) \
printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
#else
#define dprintf(format, arg...)
#endif
static int
imx_clk_sscg_pll_init(struct clknode *clk, device_t dev)
{
struct imx_clk_sscg_pll_sc *sc;
sc = clknode_get_softc(clk);
if (clknode_get_parents_num(clk) > 1) {
device_printf(clknode_get_device(clk),
"error: SSCG PLL does not support more than one parent yet\n");
return (EINVAL);
}
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
imx_clk_sscg_pll_set_gate(struct clknode *clk, bool enable)
{
struct imx_clk_sscg_pll_sc *sc;
uint32_t cfg0;
int timeout;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset + CFG0, &cfg0);
if (enable)
cfg0 &= ~(CFG0_PD);
else
cfg0 |= CFG0_PD;
WRITE4(clk, sc->offset + CFG0, cfg0);
/* Reading lock */
if (enable) {
for (timeout = 1000; timeout; timeout--) {
READ4(clk, sc->offset + CFG0, &cfg0);
if (cfg0 & CFG0_PLL_LOCK)
break;
DELAY(1);
}
}
DEVICE_UNLOCK(clk);
return (0);
}
static int
imx_clk_sscg_pll_recalc(struct clknode *clk, uint64_t *freq)
{
struct imx_clk_sscg_pll_sc *sc;
uint32_t cfg0, cfg2;
int divr1, divr2, divf1, divf2, div;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset + CFG0, &cfg0);
READ4(clk, sc->offset + CFG2, &cfg2);
DEVICE_UNLOCK(clk);
/* PLL is bypassed */
if (cfg0 & CFG0_BYPASS2)
return (0);
divr1 = (cfg2 & CFG2_DIVR1_MASK) >> CFG2_DIVR1_SHIFT;
divr2 = (cfg2 & CFG2_DIVR2_MASK) >> CFG2_DIVR2_SHIFT;
divf1 = (cfg2 & CFG2_DIVF1_MASK) >> CFG2_DIVF1_SHIFT;
divf2 = (cfg2 & CFG2_DIVF2_MASK) >> CFG2_DIVF2_SHIFT;
div = (cfg2 & CFG2_DIV_MASK) >> CFG2_DIV_SHIFT;
if (cfg0 & CFG0_BYPASS1) {
*freq = *freq / ((divr2 + 1) * (div + 1));
return (0);
}
*freq *= 2 * (divf1 + 1) * (divf2 + 1);
*freq /= (divr1 + 1) * (divr2 + 1) * (div + 1);
return (0);
}
static clknode_method_t imx_clk_sscg_pll_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, imx_clk_sscg_pll_init),
CLKNODEMETHOD(clknode_set_gate, imx_clk_sscg_pll_set_gate),
CLKNODEMETHOD(clknode_recalc_freq, imx_clk_sscg_pll_recalc),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(imx_clk_sscg_pll_clknode, imx_clk_sscg_pll_clknode_class,
imx_clk_sscg_pll_clknode_methods, sizeof(struct imx_clk_sscg_pll_sc),
clknode_class);
int
imx_clk_sscg_pll_register(struct clkdom *clkdom,
struct imx_clk_sscg_pll_def *clkdef)
{
struct clknode *clk;
struct imx_clk_sscg_pll_sc *sc;
clk = clknode_create(clkdom, &imx_clk_sscg_pll_clknode_class,
&clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,42 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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$
*/
#ifndef _IMX_CLK_SSCG_PLL_H_
#define _IMX_CLK_SSCG_PLL_H_
#include <dev/extres/clk/clk.h>
struct imx_clk_sscg_pll_def {
struct clknode_init_def clkdef;
uint32_t offset;
};
int imx_clk_sscg_pll_register(struct clkdom *clkdom, struct imx_clk_sscg_pll_def *clkdef);
#endif /* _IMX_CLK_SSCG_PLL_H_ */

View File

@ -0,0 +1,263 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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 <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "pic_if.h"
struct imx7gpc_softc {
device_t dev;
struct resource *memres;
device_t parent;
};
static struct ofw_compat_data compat_data[] = {
{ "fsl,imx7gpc", 1},
{ "fsl,imx8mq-gpc", 1},
{ NULL, 0}
};
static inline uint32_t
imx7gpc_read_4(struct imx7gpc_softc *sc, int reg)
{
return (bus_read_4(sc->memres, reg));
}
static inline void
imx7gpc_write_4(struct imx7gpc_softc *sc, int reg, uint32_t val)
{
bus_write_4(sc->memres, reg, val);
}
static int
imx7gpc_activate_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
}
static void
imx7gpc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
PIC_DISABLE_INTR(sc->parent, isrc);
}
static void
imx7gpc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
PIC_ENABLE_INTR(sc->parent, isrc);
}
static int
imx7gpc_map_intr(device_t dev, struct intr_map_data *data,
struct intr_irqsrc **isrcp)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_MAP_INTR(sc->parent, data, isrcp));
}
static int
imx7gpc_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
}
static int
imx7gpc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
}
static int
imx7gpc_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
}
static void
imx7gpc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
PIC_PRE_ITHREAD(sc->parent, isrc);
}
static void
imx7gpc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
PIC_POST_ITHREAD(sc->parent, isrc);
}
static void
imx7gpc_post_filter(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
PIC_POST_FILTER(sc->parent, isrc);
}
#ifdef SMP
static int
imx7gpc_bind_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
return (PIC_BIND_INTR(sc->parent, isrc));
}
#endif
static int
imx7gpc_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, "General Power Controller");
return (BUS_PROBE_DEFAULT);
}
static int
imx7gpc_attach(device_t dev)
{
struct imx7gpc_softc *sc = device_get_softc(dev);
phandle_t node;
phandle_t parent_xref;
int i, rv;
sc->dev = dev;
node = ofw_bus_get_node(dev);
rv = OF_getencprop(node, "interrupt-parent", &parent_xref,
sizeof(parent_xref));
if (rv <= 0) {
device_printf(dev, "Can't read parent node property\n");
return (ENXIO);
}
sc->parent = OF_device_from_xref(parent_xref);
if (sc->parent == NULL) {
device_printf(dev, "Can't find parent controller\n");
return (ENXIO);
}
i = 0;
sc->memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i,
RF_ACTIVE);
if (sc->memres == NULL) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
/* TODO: power up OTG domain and unmask all interrupts */
if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, i, sc->memres);
device_printf(dev, "Cannot register PIC\n");
return (ENXIO);
}
return (0);
}
static device_method_t imx7gpc_methods[] = {
DEVMETHOD(device_probe, imx7gpc_probe),
DEVMETHOD(device_attach, imx7gpc_attach),
/* Interrupt controller interface */
DEVMETHOD(pic_activate_intr, imx7gpc_activate_intr),
DEVMETHOD(pic_disable_intr, imx7gpc_disable_intr),
DEVMETHOD(pic_enable_intr, imx7gpc_enable_intr),
DEVMETHOD(pic_map_intr, imx7gpc_map_intr),
DEVMETHOD(pic_deactivate_intr, imx7gpc_deactivate_intr),
DEVMETHOD(pic_setup_intr, imx7gpc_setup_intr),
DEVMETHOD(pic_teardown_intr, imx7gpc_teardown_intr),
DEVMETHOD(pic_pre_ithread, imx7gpc_pre_ithread),
DEVMETHOD(pic_post_ithread, imx7gpc_post_ithread),
DEVMETHOD(pic_post_filter, imx7gpc_post_filter),
#ifdef SMP
DEVMETHOD(pic_bind_intr, imx7gpc_bind_intr),
#endif
DEVMETHOD_END
};
static driver_t imx7gpc_driver = {
"imx7gpc",
imx7gpc_methods,
sizeof(struct imx7gpc_softc),
};
static devclass_t imx7gpc_devclass;
EARLY_DRIVER_MODULE(imx7gpc, ofwbus, imx7gpc_driver, imx7gpc_devclass, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
EARLY_DRIVER_MODULE(imx7gpc, simplebus, imx7gpc_driver, imx7gpc_devclass, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

View File

@ -0,0 +1,484 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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$");
/*
* Clocks driver for Freescale i.MX8MQ SoC
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <machine/bus.h>
#include <arm64/freescale/imx/imx_ccm_clk.h>
#include <arm64/freescale/imx/imx8mq_ccm.h>
#include <arm64/freescale/imx/clk/imx_clk_gate.h>
#include <arm64/freescale/imx/clk/imx_clk_mux.h>
#include <arm64/freescale/imx/clk/imx_clk_composite.h>
#include <arm64/freescale/imx/clk/imx_clk_sscg_pll.h>
#include <arm64/freescale/imx/clk/imx_clk_frac_pll.h>
#include "clkdev_if.h"
static const char *pll_ref_p[] = {
"osc_25m", "osc_27m", "dummy", "dummy"
};
static const char *sys3_pll_out_p[] = {
"sys3_pll1_ref_sel"
};
static const char * arm_pll_bypass_p[] = {
"arm_pll", "arm_pll_ref_sel"
};
static const char * gpu_pll_bypass_p[] = {
"gpu_pll", "gpu_pll_ref_sel"
};
static const char * vpu_pll_bypass_p[] = {
"vpu_pll", "vpu_pll_ref_sel"
};
static const char * audio_pll1_bypass_p[] = {
"audio_pll1", "audio_pll1_ref_sel"
};
static const char * audio_pll2_bypass_p[] = {
"audio_pll2", "audio_pll2_ref_sel"
};
static const char * video_pll1_bypass_p[] = {
"video_pll1", "video_pll1_ref_sel"
};
static const char *uart_p[] = {
"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll_out",
"clk_ext2", "clk_ext4", "audio_pll2_out"
};
static const char *usdhc_p[] = {
"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", "audio_pll2_out",
"sys1_pll_266m", "sys3_pll_out", "sys1_pll_100m"
};
static const char *enet_axi_p[] = {
"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m", "sys2_pll_200m",
"audio_pll1_out", "video_pll1_out", "sys3_pll_out"
};
static const char *enet_ref_p[] = {
"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m", "sys1_pll_160m",
"audio_pll1_out", "video_pll1_out", "clk_ext4"
};
static const char *enet_timer_p[] = {
"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "video_pll1_out"
};
static const char *enet_phy_ref_p[] = {
"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out"
};
static const char *usb_bus_p[] = {
"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m", "sys2_pll_200m",
"clk_ext2", "clk_ext4", "audio_pll2_out"
};
static const char *usb_core_phy_p[] = {
"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", "sys2_pll_200m",
"clk_ext2", "clk_ext3", "audio_pll2_out"
};
static const char *i2c_p[] = {
"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m"
};
static const char *ahb_p[] = {
"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m", "sys2_pll_125m",
"sys3_pll_out", "audio_pll1_out", "video_pll1_out"
};
static struct imx_clk imx_clks[] = {
FIXED(IMX8MQ_CLK_DUMMY, "dummy", 0),
LINK(IMX8MQ_CLK_32K, "ckil"),
LINK(IMX8MQ_CLK_25M, "osc_25m"),
LINK(IMX8MQ_CLK_27M, "osc_27m"),
LINK(IMX8MQ_CLK_EXT1, "clk_ext1"),
LINK(IMX8MQ_CLK_EXT2, "clk_ext2"),
LINK(IMX8MQ_CLK_EXT3, "clk_ext3"),
LINK(IMX8MQ_CLK_EXT4, "clk_ext4"),
FIXED(IMX8MQ_SYS1_PLL_OUT, "sys1_pll_out", 800000000),
FIXED(IMX8MQ_SYS2_PLL_OUT, "sys2_pll_out", 1000000000),
SSCG_PLL(IMX8MQ_SYS3_PLL_OUT, "sys3_pll_out", sys3_pll_out_p, 0x48),
MUX(IMX8MQ_ARM_PLL_REF_SEL, "arm_pll_ref_sel", pll_ref_p, 0, 0x28, 16, 2),
MUX(IMX8MQ_GPU_PLL_REF_SEL, "gpu_pll_ref_sel", pll_ref_p, 0, 0x18, 16, 2),
MUX(IMX8MQ_VPU_PLL_REF_SEL, "vpu_pll_ref_sel", pll_ref_p, 0, 0x20, 16, 2),
MUX(IMX8MQ_AUDIO_PLL1_REF_SEL, "audio_pll1_ref_sel", pll_ref_p, 0, 0x0, 16, 2),
MUX(IMX8MQ_AUDIO_PLL2_REF_SEL, "audio_pll2_ref_sel", pll_ref_p, 0, 0x8, 16, 2),
MUX(IMX8MQ_VIDEO_PLL1_REF_SEL, "video_pll1_ref_sel", pll_ref_p, 0, 0x10, 16, 2),
MUX(IMX8MQ_SYS3_PLL1_REF_SEL, "sys3_pll1_ref_sel", pll_ref_p, 0, 0x48, 0, 2),
MUX(IMX8MQ_DRAM_PLL1_REF_SEL, "dram_pll1_ref_sel", pll_ref_p, 0, 0x60, 0, 2),
MUX(IMX8MQ_VIDEO2_PLL1_REF_SEL, "video2_pll1_ref_sel", pll_ref_p, 0, 0x54, 0, 2),
DIV(IMX8MQ_ARM_PLL_REF_DIV, "arm_pll_ref_div", "arm_pll_ref_sel", 0x28, 5, 6),
DIV(IMX8MQ_GPU_PLL_REF_DIV, "gpu_pll_ref_div", "gpu_pll_ref_sel", 0x18, 5, 6),
DIV(IMX8MQ_VPU_PLL_REF_DIV, "vpu_pll_ref_div", "vpu_pll_ref_sel", 0x20, 5, 6),
DIV(IMX8MQ_AUDIO_PLL1_REF_DIV, "audio_pll1_ref_div", "audio_pll1_ref_sel", 0x0, 5, 6),
DIV(IMX8MQ_AUDIO_PLL2_REF_DIV, "audio_pll2_ref_div", "audio_pll2_ref_sel", 0x8, 5, 6),
DIV(IMX8MQ_VIDEO_PLL1_REF_DIV, "video_pll1_ref_div", "video_pll1_ref_sel", 0x10, 5, 6),
FRAC_PLL(IMX8MQ_ARM_PLL, "arm_pll", "arm_pll_ref_div", 0x28),
FRAC_PLL(IMX8MQ_GPU_PLL, "gpu_pll", "gpu_pll_ref_div", 0x18),
FRAC_PLL(IMX8MQ_VPU_PLL, "vpu_pll", "vpu_pll_ref_div", 0x20),
FRAC_PLL(IMX8MQ_AUDIO_PLL1, "audio_pll1", "audio_pll1_ref_div", 0x0),
FRAC_PLL(IMX8MQ_AUDIO_PLL2, "audio_pll2", "audio_pll2_ref_div", 0x8),
FRAC_PLL(IMX8MQ_VIDEO_PLL1, "video_pll1", "video_pll1_ref_div", 0x10),
/* ARM_PLL needs SET_PARENT flag */
MUX(IMX8MQ_ARM_PLL_BYPASS, "arm_pll_bypass", arm_pll_bypass_p, 0, 0x28, 14, 1),
MUX(IMX8MQ_GPU_PLL_BYPASS, "gpu_pll_bypass", gpu_pll_bypass_p, 0, 0x18, 14, 1),
MUX(IMX8MQ_VPU_PLL_BYPASS, "vpu_pll_bypass", vpu_pll_bypass_p, 0, 0x20, 14, 1),
MUX(IMX8MQ_AUDIO_PLL1_BYPASS, "audio_pll1_bypass", audio_pll1_bypass_p, 0, 0x0, 14, 1),
MUX(IMX8MQ_AUDIO_PLL2_BYPASS, "audio_pll2_bypass", audio_pll2_bypass_p, 0, 0x8, 14, 1),
MUX(IMX8MQ_VIDEO_PLL1_BYPASS, "video_pll1_bypass", video_pll1_bypass_p, 0, 0x10, 14, 1),
GATE(IMX8MQ_ARM_PLL_OUT, "arm_pll_out", "arm_pll_bypass", 0x28, 21),
GATE(IMX8MQ_GPU_PLL_OUT, "gpu_pll_out", "gpu_pll_bypass", 0x18, 21),
GATE(IMX8MQ_VPU_PLL_OUT, "vpu_pll_out", "vpu_pll_bypass", 0x20, 21),
GATE(IMX8MQ_AUDIO_PLL1_OUT, "audio_pll1_out", "audio_pll1_bypass", 0x0, 21),
GATE(IMX8MQ_AUDIO_PLL2_OUT, "audio_pll2_out", "audio_pll2_bypass", 0x8, 21),
GATE(IMX8MQ_VIDEO_PLL1_OUT, "video_pll1_out", "video_pll1_bypass", 0x10, 21),
GATE(IMX8MQ_SYS1_PLL_40M_CG, "sys1_pll_40m_cg", "sys1_pll_out", 0x30, 9),
GATE(IMX8MQ_SYS1_PLL_80M_CG, "sys1_pll_80m_cg", "sys1_pll_out", 0x30, 11),
GATE(IMX8MQ_SYS1_PLL_100M_CG, "sys1_pll_100m_cg", "sys1_pll_out", 0x30, 13),
GATE(IMX8MQ_SYS1_PLL_133M_CG, "sys1_pll_133m_cg", "sys1_pll_out", 0x30, 15),
GATE(IMX8MQ_SYS1_PLL_160M_CG, "sys1_pll_160m_cg", "sys1_pll_out", 0x30, 17),
GATE(IMX8MQ_SYS1_PLL_200M_CG, "sys1_pll_200m_cg", "sys1_pll_out", 0x30, 19),
GATE(IMX8MQ_SYS1_PLL_266M_CG, "sys1_pll_266m_cg", "sys1_pll_out", 0x30, 21),
GATE(IMX8MQ_SYS1_PLL_400M_CG, "sys1_pll_400m_cg", "sys1_pll_out", 0x30, 23),
GATE(IMX8MQ_SYS1_PLL_800M_CG, "sys1_pll_800m_cg", "sys1_pll_out", 0x30, 25),
FFACT(IMX8MQ_SYS1_PLL_40M, "sys1_pll_40m", "sys1_pll_40m_cg", 1, 20),
FFACT(IMX8MQ_SYS1_PLL_80M, "sys1_pll_80m", "sys1_pll_80m_cg", 1, 10),
FFACT(IMX8MQ_SYS1_PLL_100M, "sys1_pll_100m", "sys1_pll_100m_cg", 1, 8),
FFACT(IMX8MQ_SYS1_PLL_133M, "sys1_pll_133m", "sys1_pll_133m_cg", 1, 6),
FFACT(IMX8MQ_SYS1_PLL_160M, "sys1_pll_160m", "sys1_pll_160m_cg", 1, 5),
FFACT(IMX8MQ_SYS1_PLL_200M, "sys1_pll_200m", "sys1_pll_200m_cg", 1, 4),
FFACT(IMX8MQ_SYS1_PLL_266M, "sys1_pll_266m", "sys1_pll_266m_cg", 1, 3),
FFACT(IMX8MQ_SYS1_PLL_400M, "sys1_pll_400m", "sys1_pll_400m_cg", 1, 2),
FFACT(IMX8MQ_SYS1_PLL_800M, "sys1_pll_800m", "sys1_pll_800m_cg", 1, 1),
GATE(IMX8MQ_SYS2_PLL_50M_CG, "sys2_pll_50m_cg", "sys2_pll_out", 0x3c, 9),
GATE(IMX8MQ_SYS2_PLL_100M_CG, "sys2_pll_100m_cg", "sys2_pll_out", 0x3c, 11),
GATE(IMX8MQ_SYS2_PLL_125M_CG, "sys2_pll_125m_cg", "sys2_pll_out", 0x3c, 13),
GATE(IMX8MQ_SYS2_PLL_166M_CG, "sys2_pll_166m_cg", "sys2_pll_out", 0x3c, 15),
GATE(IMX8MQ_SYS2_PLL_200M_CG, "sys2_pll_200m_cg", "sys2_pll_out", 0x3c, 17),
GATE(IMX8MQ_SYS2_PLL_250M_CG, "sys2_pll_250m_cg", "sys2_pll_out", 0x3c, 19),
GATE(IMX8MQ_SYS2_PLL_333M_CG, "sys2_pll_333m_cg", "sys2_pll_out", 0x3c, 21),
GATE(IMX8MQ_SYS2_PLL_500M_CG, "sys2_pll_500m_cg", "sys2_pll_out", 0x3c, 23),
GATE(IMX8MQ_SYS2_PLL_1000M_CG, "sys2_pll_1000m_cg", "sys2_pll_out", 0x3c, 25),
FFACT(IMX8MQ_SYS2_PLL_50M, "sys2_pll_50m", "sys2_pll_50m_cg", 1, 20),
FFACT(IMX8MQ_SYS2_PLL_100M, "sys2_pll_100m", "sys2_pll_100m_cg", 1, 10),
FFACT(IMX8MQ_SYS2_PLL_125M, "sys2_pll_125m", "sys2_pll_125m_cg", 1, 8),
FFACT(IMX8MQ_SYS2_PLL_166M, "sys2_pll_166m", "sys2_pll_166m_cg", 1, 6),
FFACT(IMX8MQ_SYS2_PLL_200M, "sys2_pll_200m", "sys2_pll_200m_cg", 1, 5),
FFACT(IMX8MQ_SYS2_PLL_250M, "sys2_pll_250m", "sys2_pll_250m_cg", 1, 4),
FFACT(IMX8MQ_SYS2_PLL_333M, "sys2_pll_333m", "sys2_pll_333m_cg", 1, 3),
FFACT(IMX8MQ_SYS2_PLL_500M, "sys2_pll_500m", "sys2_pll_500m_cg", 1, 2),
FFACT(IMX8MQ_SYS2_PLL_1000M, "sys2_pll_1000m", "sys2_pll_1000m_cg", 1, 1),
COMPOSITE(IMX8MQ_CLK_AHB, "ahb", ahb_p, 0x9000, 0),
DIV(IMX8MQ_CLK_IPG_ROOT, "ipg_root", "ahb", 0x9080, 0, 1),
COMPOSITE(IMX8MQ_CLK_UART1, "uart1", uart_p, 0xaf00, 0),
COMPOSITE(IMX8MQ_CLK_UART2, "uart2", uart_p, 0xaf80, 0),
COMPOSITE(IMX8MQ_CLK_UART3, "uart3", uart_p, 0xb000, 0),
COMPOSITE(IMX8MQ_CLK_UART4, "uart4", uart_p, 0xb080, 0),
ROOT_GATE(IMX8MQ_CLK_UART1_ROOT, "uart1_root_clk", "uart1", 0x4490),
ROOT_GATE(IMX8MQ_CLK_UART2_ROOT, "uart2_root_clk", "uart2", 0x44a0),
ROOT_GATE(IMX8MQ_CLK_UART3_ROOT, "uart3_root_clk", "uart3", 0x44b0),
ROOT_GATE(IMX8MQ_CLK_UART4_ROOT, "uart4_root_clk", "uart4", 0x44c0),
COMPOSITE(IMX8MQ_CLK_USDHC1, "usdhc1", usdhc_p, 0xac00, CLK_SET_ROUND_DOWN),
COMPOSITE(IMX8MQ_CLK_USDHC2, "usdhc2", usdhc_p, 0xac80, CLK_SET_ROUND_DOWN),
ROOT_GATE(IMX8MQ_CLK_USDHC1_ROOT, "usdhc1_root_clk", "usdhc1", 0x4510),
ROOT_GATE(IMX8MQ_CLK_USDHC2_ROOT, "usdhc2_root_clk", "usdhc2", 0x4520),
COMPOSITE(IMX8MQ_CLK_ENET_AXI, "enet_axi", enet_axi_p, 0x8800, 0),
COMPOSITE(IMX8MQ_CLK_ENET_REF, "enet_ref", enet_ref_p, 0xa980, 0),
COMPOSITE(IMX8MQ_CLK_ENET_TIMER, "enet_timer", enet_timer_p, 0xaa00, 0),
COMPOSITE(IMX8MQ_CLK_ENET_PHY_REF, "enet_phy_ref", enet_phy_ref_p, 0xaa80, 0),
ROOT_GATE(IMX8MQ_CLK_ENET1_ROOT, "enet1_root_clk", "enet_axi", 0x40a0),
COMPOSITE(IMX8MQ_CLK_USB_BUS, "usb_bus", usb_bus_p, 0x8b80, 0),
COMPOSITE(IMX8MQ_CLK_USB_CORE_REF, "usb_core_ref", usb_core_phy_p, 0xb100, 0),
COMPOSITE(IMX8MQ_CLK_USB_PHY_REF, "usb_phy_ref", usb_core_phy_p, 0xb180, 0),
ROOT_GATE(IMX8MQ_CLK_USB1_CTRL_ROOT, "usb1_ctrl_root_clk", "usb_bus", 0x44d0),
ROOT_GATE(IMX8MQ_CLK_USB2_CTRL_ROOT, "usb2_ctrl_root_clk", "usb_bus", 0x44e0),
ROOT_GATE(IMX8MQ_CLK_USB1_PHY_ROOT, "usb1_phy_root_clk", "usb_phy_ref", 0x44f0),
ROOT_GATE(IMX8MQ_CLK_USB2_PHY_ROOT, "usb2_phy_root_clk", "usb_phy_ref", 0x4500),
COMPOSITE(IMX8MQ_CLK_I2C1, "i2c1", i2c_p, 0xad00, 0),
COMPOSITE(IMX8MQ_CLK_I2C2, "i2c2", i2c_p, 0xad80, 0),
COMPOSITE(IMX8MQ_CLK_I2C3, "i2c3", i2c_p, 0xae00, 0),
COMPOSITE(IMX8MQ_CLK_I2C4, "i2c4", i2c_p, 0xae80, 0),
ROOT_GATE(IMX8MQ_CLK_I2C1_ROOT, "i2c1_root_clk", "i2c1", 0x4170),
ROOT_GATE(IMX8MQ_CLK_I2C2_ROOT, "i2c2_root_clk", "i2c2", 0x4180),
ROOT_GATE(IMX8MQ_CLK_I2C3_ROOT, "i2c3_root_clk", "i2c3", 0x4190),
ROOT_GATE(IMX8MQ_CLK_I2C4_ROOT, "i2c4_root_clk", "i2c4", 0x41a0),
ROOT_GATE(IMX8MQ_CLK_GPIO1_ROOT, "gpio1_root_clk", "ipg_root", 0x40b0),
ROOT_GATE(IMX8MQ_CLK_GPIO2_ROOT, "gpio2_root_clk", "ipg_root", 0x40c0),
ROOT_GATE(IMX8MQ_CLK_GPIO3_ROOT, "gpio3_root_clk", "ipg_root", 0x40d0),
ROOT_GATE(IMX8MQ_CLK_GPIO4_ROOT, "gpio4_root_clk", "ipg_root", 0x40e0),
ROOT_GATE(IMX8MQ_CLK_GPIO5_ROOT, "gpio5_root_clk", "ipg_root", 0x40f0),
};
struct ccm_softc {
device_t dev;
struct resource *mem_res;
struct clkdom *clkdom;
struct mtx mtx;
struct imx_clk *clks;
int nclks;
};
static inline uint32_t
CCU_READ4(struct ccm_softc *sc, bus_size_t off)
{
return (bus_read_4(sc->mem_res, off));
}
static inline void
CCU_WRITE4(struct ccm_softc *sc, bus_size_t off, uint32_t val)
{
bus_write_4(sc->mem_res, off, val);
}
static int
ccm_detach(device_t dev)
{
struct ccm_softc *sc;
sc = device_get_softc(dev);
if (sc->mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
return (0);
}
static int
ccm_attach(device_t dev)
{
struct ccm_softc *sc;
int err, rid;
phandle_t node;
int i;
sc = device_get_softc(dev);
err = 0;
/* Allocate bus_space resources. */
rid = 0;
sc->clks = imx_clks;
sc->nclks = nitems(imx_clks);
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");
err = ENXIO;
goto out;
}
mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
sc->clkdom = clkdom_create(dev);
if (sc->clkdom == NULL)
panic("Cannot create clkdom\n");
for (i = 0; i < sc->nclks; i++) {
switch (sc->clks[i].type) {
case IMX_CLK_UNDEFINED:
break;
case IMX_CLK_LINK:
clknode_link_register(sc->clkdom,
sc->clks[i].clk.link);
break;
case IMX_CLK_FIXED:
clknode_fixed_register(sc->clkdom,
sc->clks[i].clk.fixed);
break;
case IMX_CLK_MUX:
imx_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux);
break;
case IMX_CLK_GATE:
imx_clk_gate_register(sc->clkdom, sc->clks[i].clk.gate);
break;
case IMX_CLK_COMPOSITE:
imx_clk_composite_register(sc->clkdom, sc->clks[i].clk.composite);
break;
case IMX_CLK_SSCG_PLL:
imx_clk_sscg_pll_register(sc->clkdom, sc->clks[i].clk.sscg_pll);
break;
case IMX_CLK_FRAC_PLL:
imx_clk_frac_pll_register(sc->clkdom, sc->clks[i].clk.frac_pll);
break;
case IMX_CLK_DIV:
clknode_div_register(sc->clkdom, sc->clks[i].clk.div);
break;
default:
device_printf(dev, "Unknown clock type %d\n", sc->clks[i].type);
return (ENXIO);
}
}
if (clkdom_finit(sc->clkdom) != 0)
panic("cannot finalize clkdom initialization\n");
if (bootverbose)
clkdom_dump(sc->clkdom);
node = ofw_bus_get_node(dev);
clk_set_assigned(dev, node);
err = 0;
out:
if (err != 0)
ccm_detach(dev);
return (err);
}
static int
ccm_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "fsl,imx8mq-ccm") == 0)
return (ENXIO);
device_set_desc(dev, "Freescale i.MX8 Clock Control Module");
return (BUS_PROBE_DEFAULT);
}
static int
imx_ccm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct ccm_softc *sc;
sc = device_get_softc(dev);
CCU_WRITE4(sc, addr, val);
return (0);
}
static int
imx_ccm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct ccm_softc *sc;
sc = device_get_softc(dev);
*val = CCU_READ4(sc, addr);
return (0);
}
static int
imx_ccm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
{
struct ccm_softc *sc;
uint32_t reg;
sc = device_get_softc(dev);
reg = CCU_READ4(sc, addr);
reg &= ~clr;
reg |= set;
CCU_WRITE4(sc, addr, reg);
return (0);
}
static void
imx_ccm_device_lock(device_t dev)
{
struct ccm_softc *sc;
sc = device_get_softc(dev);
mtx_lock(&sc->mtx);
}
static void
imx_ccm_device_unlock(device_t dev)
{
struct ccm_softc *sc;
sc = device_get_softc(dev);
mtx_unlock(&sc->mtx);
}
static device_method_t ccm_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ccm_probe),
DEVMETHOD(device_attach, ccm_attach),
DEVMETHOD(device_detach, ccm_detach),
/* clkdev interface */
DEVMETHOD(clkdev_write_4, imx_ccm_write_4),
DEVMETHOD(clkdev_read_4, imx_ccm_read_4),
DEVMETHOD(clkdev_modify_4, imx_ccm_modify_4),
DEVMETHOD(clkdev_device_lock, imx_ccm_device_lock),
DEVMETHOD(clkdev_device_unlock, imx_ccm_device_unlock),
DEVMETHOD_END
};
static driver_t ccm_driver = {
"ccm",
ccm_methods,
sizeof(struct ccm_softc)
};
static devclass_t ccm_devclass;
EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0,
BUS_PASS_CPU + BUS_PASS_ORDER_EARLY);

View File

@ -0,0 +1,173 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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$
*/
#ifndef __IMX8MQ_CCM_H__
#define __IMX8MQ_CCM_H__
#define IMX8MQ_CLK_DUMMY 0
#define IMX8MQ_CLK_32K 1
#define IMX8MQ_CLK_25M 2
#define IMX8MQ_CLK_27M 3
#define IMX8MQ_CLK_EXT1 4
#define IMX8MQ_CLK_EXT2 5
#define IMX8MQ_CLK_EXT3 6
#define IMX8MQ_CLK_EXT4 7
#define IMX8MQ_ARM_PLL_REF_SEL 8
#define IMX8MQ_ARM_PLL_REF_DIV 9
#define IMX8MQ_ARM_PLL 10
#define IMX8MQ_ARM_PLL_BYPASS 11
#define IMX8MQ_ARM_PLL_OUT 12
#define IMX8MQ_GPU_PLL_REF_SEL 13
#define IMX8MQ_GPU_PLL_REF_DIV 14
#define IMX8MQ_GPU_PLL 15
#define IMX8MQ_GPU_PLL_BYPASS 16
#define IMX8MQ_GPU_PLL_OUT 17
#define IMX8MQ_VPU_PLL_REF_SEL 18
#define IMX8MQ_VPU_PLL_REF_DIV 19
#define IMX8MQ_VPU_PLL 20
#define IMX8MQ_VPU_PLL_BYPASS 21
#define IMX8MQ_VPU_PLL_OUT 22
#define IMX8MQ_AUDIO_PLL1_REF_SEL 23
#define IMX8MQ_AUDIO_PLL1_REF_DIV 24
#define IMX8MQ_AUDIO_PLL1 25
#define IMX8MQ_AUDIO_PLL1_BYPASS 26
#define IMX8MQ_AUDIO_PLL1_OUT 27
#define IMX8MQ_AUDIO_PLL2_REF_SEL 28
#define IMX8MQ_AUDIO_PLL2_REF_DIV 29
#define IMX8MQ_AUDIO_PLL2 30
#define IMX8MQ_AUDIO_PLL2_BYPASS 31
#define IMX8MQ_AUDIO_PLL2_OUT 32
#define IMX8MQ_VIDEO_PLL1_REF_SEL 33
#define IMX8MQ_VIDEO_PLL1_REF_DIV 34
#define IMX8MQ_VIDEO_PLL1 35
#define IMX8MQ_VIDEO_PLL1_BYPASS 36
#define IMX8MQ_VIDEO_PLL1_OUT 37
#define IMX8MQ_SYS3_PLL1_REF_SEL 54
#define IMX8MQ_SYS3_PLL1 56
#define IMX8MQ_DRAM_PLL1_REF_SEL 62
#define IMX8MQ_SYS1_PLL_40M 70
#define IMX8MQ_SYS1_PLL_80M 71
#define IMX8MQ_SYS1_PLL_100M 72
#define IMX8MQ_SYS1_PLL_133M 73
#define IMX8MQ_SYS1_PLL_160M 74
#define IMX8MQ_SYS1_PLL_200M 75
#define IMX8MQ_SYS1_PLL_266M 76
#define IMX8MQ_SYS1_PLL_400M 77
#define IMX8MQ_SYS1_PLL_800M 78
#define IMX8MQ_SYS2_PLL_50M 79
#define IMX8MQ_SYS2_PLL_100M 80
#define IMX8MQ_SYS2_PLL_125M 81
#define IMX8MQ_SYS2_PLL_166M 82
#define IMX8MQ_SYS2_PLL_200M 83
#define IMX8MQ_SYS2_PLL_250M 84
#define IMX8MQ_SYS2_PLL_333M 85
#define IMX8MQ_SYS2_PLL_500M 86
#define IMX8MQ_SYS2_PLL_1000M 87
#define IMX8MQ_CLK_ENET_AXI 104
#define IMX8MQ_CLK_USB_BUS 110
#define IMX8MQ_CLK_AHB 116
#define IMX8MQ_CLK_ENET_REF 137
#define IMX8MQ_CLK_ENET_TIMER 138
#define IMX8MQ_CLK_ENET_PHY_REF 139
#define IMX8MQ_CLK_USDHC1 142
#define IMX8MQ_CLK_USDHC2 143
#define IMX8MQ_CLK_I2C1 144
#define IMX8MQ_CLK_I2C2 145
#define IMX8MQ_CLK_I2C3 146
#define IMX8MQ_CLK_I2C4 147
#define IMX8MQ_CLK_UART1 148
#define IMX8MQ_CLK_UART2 149
#define IMX8MQ_CLK_UART3 150
#define IMX8MQ_CLK_UART4 151
#define IMX8MQ_CLK_USB_CORE_REF 152
#define IMX8MQ_CLK_USB_PHY_REF 153
#define IMX8MQ_CLK_ENET1_ROOT 182
#define IMX8MQ_CLK_I2C1_ROOT 184
#define IMX8MQ_CLK_I2C2_ROOT 185
#define IMX8MQ_CLK_I2C3_ROOT 186
#define IMX8MQ_CLK_I2C4_ROOT 187
#define IMX8MQ_CLK_UART1_ROOT 202
#define IMX8MQ_CLK_UART2_ROOT 203
#define IMX8MQ_CLK_UART3_ROOT 204
#define IMX8MQ_CLK_UART4_ROOT 205
#define IMX8MQ_CLK_USB1_CTRL_ROOT 206
#define IMX8MQ_CLK_USB2_CTRL_ROOT 207
#define IMX8MQ_CLK_USB1_PHY_ROOT 208
#define IMX8MQ_CLK_USB2_PHY_ROOT 209
#define IMX8MQ_CLK_USDHC1_ROOT 210
#define IMX8MQ_CLK_USDHC2_ROOT 211
#define IMX8MQ_SYS1_PLL_OUT 231
#define IMX8MQ_SYS2_PLL_OUT 232
#define IMX8MQ_SYS3_PLL_OUT 233
#define IMX8MQ_CLK_IPG_ROOT 236
#define IMX8MQ_CLK_GPIO1_ROOT 259
#define IMX8MQ_CLK_GPIO2_ROOT 260
#define IMX8MQ_CLK_GPIO3_ROOT 261
#define IMX8MQ_CLK_GPIO4_ROOT 262
#define IMX8MQ_CLK_GPIO5_ROOT 263
#define IMX8MQ_VIDEO2_PLL1_REF_SEL 266
#define IMX8MQ_SYS1_PLL_40M_CG 267
#define IMX8MQ_SYS1_PLL_80M_CG 268
#define IMX8MQ_SYS1_PLL_100M_CG 269
#define IMX8MQ_SYS1_PLL_133M_CG 270
#define IMX8MQ_SYS1_PLL_160M_CG 271
#define IMX8MQ_SYS1_PLL_200M_CG 272
#define IMX8MQ_SYS1_PLL_266M_CG 273
#define IMX8MQ_SYS1_PLL_400M_CG 274
#define IMX8MQ_SYS1_PLL_800M_CG 275
#define IMX8MQ_SYS2_PLL_50M_CG 276
#define IMX8MQ_SYS2_PLL_100M_CG 277
#define IMX8MQ_SYS2_PLL_125M_CG 278
#define IMX8MQ_SYS2_PLL_166M_CG 279
#define IMX8MQ_SYS2_PLL_200M_CG 280
#define IMX8MQ_SYS2_PLL_250M_CG 281
#define IMX8MQ_SYS2_PLL_333M_CG 282
#define IMX8MQ_SYS2_PLL_500M_CG 283
#define IMX8MQ_SYS2_PLL_1000M_CG 284
#endif

View File

@ -0,0 +1,212 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@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$
*/
#ifndef IMX6_CCM_CLK_H
#define IMX6_CCM_CLK_H
#include <dev/extres/clk/clk.h>
#include <dev/extres/clk/clk_div.h>
#include <dev/extres/clk/clk_fixed.h>
#include <dev/extres/clk/clk_gate.h>
#include <dev/extres/clk/clk_link.h>
enum imx_clk_type {
IMX_CLK_UNDEFINED = 0,
IMX_CLK_FIXED,
IMX_CLK_LINK,
IMX_CLK_MUX,
IMX_CLK_GATE,
IMX_CLK_COMPOSITE,
IMX_CLK_SSCG_PLL,
IMX_CLK_FRAC_PLL,
IMX_CLK_DIV,
};
struct imx_clk {
enum imx_clk_type type;
union {
struct clk_fixed_def *fixed;
struct clk_link_def *link;
struct imx_clk_mux_def *mux;
struct imx_clk_gate_def *gate;
struct imx_clk_composite_def *composite;
struct imx_clk_sscg_pll_def *sscg_pll;
struct imx_clk_frac_pll_def *frac_pll;
struct clk_div_def *div;
} clk;
};
/* Linked clock. */
#define LINK(_id, _name) \
{ \
.type = IMX_CLK_LINK, \
.clk.link = &(struct clk_link_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = NULL, \
.clkdef.parent_cnt = 0, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
}, \
}
/* Complex clock without divider (multiplexer only). */
#define MUX(_id, _name, _pn, _f, _mo, _ms, _mw) \
{ \
.type = IMX_CLK_MUX, \
.clk.mux = &(struct imx_clk_mux_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = _pn, \
.clkdef.parent_cnt = nitems(_pn), \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _mo, \
.shift = _ms, \
.width = _mw, \
.mux_flags = _f, \
}, \
}
/* Fixed frequency clock */
#define FIXED(_id, _name, _freq) \
{ \
.type = IMX_CLK_FIXED, \
.clk.fixed = &(struct clk_fixed_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.freq = _freq, \
}, \
}
/* Fixed factor multipier/divider. */
#define FFACT(_id, _name, _pname, _mult, _div) \
{ \
.type = IMX_CLK_FIXED, \
.clk.fixed = &(struct clk_fixed_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = (const char *[]){_pname}, \
.clkdef.parent_cnt = 1, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.mult = _mult, \
.div = _div, \
}, \
}
/* Clock gate */
#define GATE(_id, _name, _pname, _o, _shift) \
{ \
.type = IMX_CLK_GATE, \
.clk.gate = &(struct imx_clk_gate_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = (const char *[]){_pname}, \
.clkdef.parent_cnt = 1, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _o, \
.shift = _shift, \
.mask = 1, \
}, \
}
/* Root clock gate */
#define ROOT_GATE(_id, _name, _pname, _reg) \
{ \
.type = IMX_CLK_GATE, \
.clk.gate = &(struct imx_clk_gate_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = (const char *[]){_pname}, \
.clkdef.parent_cnt = 1, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _reg, \
.shift = 0, \
.mask = 3, \
}, \
}
/* Composite clock with GATE, MUX, PRE_DIV, and POST_DIV */
#define COMPOSITE(_id, _name, _pn, _o, _flags) \
{ \
.type = IMX_CLK_COMPOSITE, \
.clk.composite = &(struct imx_clk_composite_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = _pn, \
.clkdef.parent_cnt = nitems(_pn), \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _o, \
.flags = _flags, \
}, \
}
/* SSCG PLL */
#define SSCG_PLL(_id, _name, _pn, _o) \
{ \
.type = IMX_CLK_SSCG_PLL, \
.clk.composite = &(struct imx_clk_composite_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = _pn, \
.clkdef.parent_cnt = nitems(_pn), \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _o, \
}, \
}
/* Fractional PLL */
#define FRAC_PLL(_id, _name, _pname, _o) \
{ \
.type = IMX_CLK_FRAC_PLL, \
.clk.frac_pll = &(struct imx_clk_frac_pll_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = (const char *[]){_pname}, \
.clkdef.parent_cnt = 1, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _o, \
}, \
}
#define DIV(_id, _name, _pname, _o, _shift, _width) \
{ \
.type = IMX_CLK_DIV, \
.clk.div = &(struct clk_div_def) { \
.clkdef.id = _id, \
.clkdef.name = _name, \
.clkdef.parent_names = (const char *[]){_pname}, \
.clkdef.parent_cnt = 1, \
.clkdef.flags = CLK_NODE_STATIC_STRINGS, \
.offset = _o, \
.i_shift = _shift, \
.i_width = _width, \
}, \
}
#endif

View File

@ -3172,6 +3172,7 @@ dev/uart/uart_bus_scc.c optional uart scc
dev/uart/uart_core.c optional uart
dev/uart/uart_cpu_acpi.c optional uart acpi
dev/uart/uart_dbg.c optional uart gdb
dev/uart/uart_dev_imx.c optional uart uart_imx fdt
dev/uart/uart_dev_msm.c optional uart uart_msm fdt
dev/uart/uart_dev_mvebu.c optional uart uart_mvebu
dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 | uart uart_snps

View File

@ -395,3 +395,18 @@ arm64/rockchip/clk/rk_clk_pll.c optional fdt soc_rockchip_rk3328 | fdt soc_rock
arm64/rockchip/clk/rk3328_cru.c optional fdt soc_rockchip_rk3328
arm64/rockchip/clk/rk3399_cru.c optional fdt soc_rockchip_rk3399
arm64/rockchip/clk/rk3399_pmucru.c optional fdt soc_rockchip_rk3399
# i.MX8 Clock support
arm64/freescale/imx/imx8mq_ccm.c optional fdt soc_freescale_imx8
arm64/freescale/imx/clk/imx_clk_gate.c optional fdt soc_freescale_imx8
arm64/freescale/imx/clk/imx_clk_mux.c optional fdt soc_freescale_imx8
arm64/freescale/imx/clk/imx_clk_composite.c optional fdt soc_freescale_imx8
arm64/freescale/imx/clk/imx_clk_sscg_pll.c optional fdt soc_freescale_imx8
arm64/freescale/imx/clk/imx_clk_frac_pll.c optional fdt soc_freescale_imx8
# iMX drivers
arm/freescale/imx/imx_gpio.c optional gpio
arm/freescale/imx/imx_i2c.c optional fsliic
arm/freescale/imx/imx_machdep.c standard
arm64/freescale/imx/imx7gpc.c optional fdt soc_freescale_imx8
dev/ffec/if_ffec.c optional ffec

View File

@ -22,6 +22,7 @@ SOC_ALLWINNER_H6 opt_soc.h
SOC_BRCM_BCM2837 opt_soc.h
SOC_BRCM_BCM2838 opt_soc.h
SOC_CAVM_THUNDERX opt_soc.h
SOC_FREESCALE_IMX8 opt_soc.h
SOC_HISI_HI6220 opt_soc.h
SOC_INTEL_STRATIX10 opt_soc.h
SOC_MARVELL_8K opt_soc.h

View File

@ -123,6 +123,7 @@ static struct ofw_compat_data compat_data[] = {
{"fsl,imx53-fec", FECTYPE_IMX53},
{"fsl,imx6q-fec", FECTYPE_IMX6 | FECFLAG_RACC | FECFLAG_GBE },
{"fsl,imx6ul-fec", FECTYPE_IMX6 | FECFLAG_RACC },
{"fsl,imx6sx-fec", FECTYPE_IMX6 | FECFLAG_RACC },
{"fsl,imx7d-fec", FECTYPE_IMX6 | FECFLAG_RACC | FECFLAG_GBE |
FECFLAG_AVB },
{"fsl,mvf600-fec", FECTYPE_MVF | FECFLAG_RACC },

View File

@ -40,13 +40,21 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/kdb.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/uart/uart.h>
#include <dev/uart/uart_cpu.h>
#include <dev/uart/uart_cpu_fdt.h>
#include <dev/uart/uart_bus.h>
#include <dev/uart/uart_dev_imx.h>
#if defined(EXT_RESOURCES) && defined(__aarch64__)
#define IMX_ENABLE_CLOCKS
#endif
#ifdef IMX_ENABLE_CLOCKS
#include <dev/extres/clk/clk.h>
#endif
#include "uart_if.h"
#include <arm/freescale/imx/imx_ccmvar.h>
@ -125,7 +133,7 @@ imx_uart_getbaud(struct uart_bas *bas)
*/
i = (GETREG(bas, REG(UFCR)) & IMXUART_UFCR_RFDIV_MASK) >>
IMXUART_UFCR_RFDIV_SHIFT;
rate = imx_ccm_uart_hz() / predivs[i];
rate = bas->rclk / predivs[i];
ubir = GETREG(bas, REG(UBIR)) + 1;
ubmr = GETREG(bas, REG(UBMR)) + 1;
baud = ((rate / 16 ) * ubir) / ubmr;
@ -193,8 +201,8 @@ imx_uart_init(struct uart_bas *bas, int baudrate, int databits,
* Note that a quirk of the hardware requires that both UBIR and UBMR be
* set back to back in order for the change to take effect.
*/
if (baudrate > 0) {
baseclk = imx_ccm_uart_hz();
if ((baudrate > 0) && (bas->rclk != 0)) {
baseclk = bas->rclk;
reg = GETREG(bas, REG(UFCR));
reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1;
SETREG(bas, REG(UFCR), reg);
@ -323,6 +331,42 @@ UART_FDT_CLASS_AND_DEVICE(compat_data);
i = (i & s) ? (i & ~s) | d : i; \
}
#ifdef IMX_ENABLE_CLOCKS
static int
imx_uart_setup_clocks(struct uart_softc *sc)
{
struct uart_bas *bas;
clk_t ipgclk, perclk;
uint64_t freq;
int error;
bas = &sc->sc_bas;
if (clk_get_by_ofw_name(sc->sc_dev, 0, "ipg", &ipgclk) != 0)
return (ENOENT);
if (clk_get_by_ofw_name(sc->sc_dev, 0, "per", &perclk) != 0) {
return (ENOENT);
}
error = clk_enable(ipgclk);
if (error != 0) {
device_printf(sc->sc_dev, "cannot enable ipg clock\n");
return (error);
}
error = clk_get_freq(perclk, &freq);
if (error != 0) {
device_printf(sc->sc_dev, "cannot get frequency\n");
return (error);
}
bas->rclk = (uint32_t)freq;
return (0);
}
#endif
static int
imx_uart_bus_attach(struct uart_softc *sc)
{
@ -330,6 +374,15 @@ imx_uart_bus_attach(struct uart_softc *sc)
struct uart_devinfo *di;
bas = &sc->sc_bas;
#ifdef IMX_ENABLE_CLOCKS
int error = imx_uart_setup_clocks(sc);
if (error)
return (error);
#else
bas->rclk = imx_ccm_uart_hz();
#endif
if (sc->sc_sysdev != NULL) {
di = sc->sc_sysdev;
imx_uart_init(bas, di->baudrate, di->databits, di->stopbits,

View File

@ -0,0 +1,7 @@
# $FreeBSD$
# All the dts files for imx8 systems we support.
DTS= \
freescale/imx8mq-evk.dts \
freescale/imx8mq-nitrogen.dts
.include <bsd.dtb.mk>