u-boot-imx-20190719

- CCF for i.MX6
- nandbcb command to write SPL into NAND
- Switch to DM (i.MX28)
- Boards: Toradex, engicam, DH
- Fixes for i.MX8
- Fixes for i.MX7ULP

Travis: https://travis-ci.org/sbabic/u-boot-imx/builds/561147504
This commit is contained in:
Tom Rini
2019-07-27 09:35:05 -04:00
152 changed files with 6917 additions and 766 deletions

View File

@@ -46,6 +46,20 @@ config CLK_BOSTON
help
Enable this to support the clocks
config SPL_CLK_CCF
bool "SPL Common Clock Framework [CCF] support "
depends on SPL_CLK_IMX6Q
help
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] code in U-Boot's SPL.
config CLK_CCF
bool "Common Clock Framework [CCF] support "
depends on CLK_IMX6Q || SANDBOX_CLK_CCF
help
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] code in U-Boot's clock driver.
config CLK_STM32F
bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4)
@@ -125,4 +139,12 @@ config CLK_MPC83XX
help
Support for the clock driver of the MPC83xx series of SoCs.
config SANDBOX_CLK_CCF
bool "Sandbox Common Clock Framework [CCF] support "
depends on SANDBOX
select CLK_CCF
help
Enable this option if you want to test the Linux kernel's Common
Clock Framework [CCF] code in U-Boot's Sandbox clock driver.
endmenu

View File

@@ -7,6 +7,8 @@
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
obj-y += analogbits/
obj-y += imx/
@@ -37,5 +39,6 @@ obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o

155
drivers/clk/clk-divider.c Normal file
View File

@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
*
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <dm/uclass.h>
#include <dm/lists.h>
#include <dm/device-internal.h>
#include <linux/clk-provider.h>
#include <div64.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
static unsigned int _get_table_div(const struct clk_div_table *table,
unsigned int val)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->val == val)
return clkt->div;
return 0;
}
static unsigned int _get_div(const struct clk_div_table *table,
unsigned int val, unsigned long flags, u8 width)
{
if (flags & CLK_DIVIDER_ONE_BASED)
return val;
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << val;
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return val ? val : clk_div_mask(width) + 1;
if (table)
return _get_table_div(table, val);
return val + 1;
}
unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
unsigned int val,
const struct clk_div_table *table,
unsigned long flags, unsigned long width)
{
unsigned int div;
div = _get_div(table, val, flags, width);
if (!div) {
WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
clk_hw_get_name(hw));
return parent_rate;
}
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
}
static ulong clk_divider_recalc_rate(struct clk *clk)
{
struct clk_divider *divider =
to_clk_divider(dev_get_clk_ptr(clk->dev));
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned int val;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
val = divider->io_divider_val;
#else
val = readl(divider->reg);
#endif
val >>= divider->shift;
val &= clk_div_mask(divider->width);
return divider_recalc_rate(clk, parent_rate, val, divider->table,
divider->flags, divider->width);
}
const struct clk_ops clk_divider_ops = {
.get_rate = clk_divider_recalc_rate,
};
static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, const struct clk_div_table *table)
{
struct clk_divider *div;
struct clk *clk;
int ret;
if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
if (width + shift > 16) {
pr_warn("divider value exceeds LOWORD field\n");
return ERR_PTR(-EINVAL);
}
}
/* allocate the divider */
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
/* struct clk_divider assignments */
div->reg = reg;
div->shift = shift;
div->width = width;
div->flags = clk_divider_flags;
div->table = table;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
div->io_divider_val = *(u32 *)reg;
#endif
/* register the clock */
clk = &div->clk;
ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
if (ret) {
kfree(div);
return ERR_PTR(ret);
}
return clk;
}
struct clk *clk_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags)
{
struct clk *clk;
clk = _register_divider(dev, name, parent_name, flags, reg, shift,
width, clk_divider_flags, NULL);
if (IS_ERR(clk))
return ERR_CAST(clk);
return clk;
}
U_BOOT_DRIVER(ccf_clk_divider) = {
.name = UBOOT_DM_CLK_CCF_DIVIDER,
.id = UCLASS_CLK,
.ops = &clk_divider_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*/
#include <common.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <linux/clk-provider.h>
#include <div64.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor"
static ulong clk_factor_recalc_rate(struct clk *clk)
{
struct clk_fixed_factor *fix =
to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned long long int rate;
rate = (unsigned long long int)parent_rate * fix->mult;
do_div(rate, fix->div);
return (ulong)rate;
}
const struct clk_ops ccf_clk_fixed_factor_ops = {
.get_rate = clk_factor_recalc_rate,
};
struct clk *clk_hw_register_fixed_factor(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
struct clk_fixed_factor *fix;
struct clk *clk;
int ret;
fix = kzalloc(sizeof(*fix), GFP_KERNEL);
if (!fix)
return ERR_PTR(-ENOMEM);
/* struct clk_fixed_factor assignments */
fix->mult = mult;
fix->div = div;
clk = &fix->clk;
ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name,
parent_name);
if (ret) {
kfree(fix);
return ERR_PTR(ret);
}
return clk;
}
struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
struct clk *clk;
clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
div);
if (IS_ERR(clk))
return ERR_CAST(clk);
return clk;
}
U_BOOT_DRIVER(imx_clk_fixed_factor) = {
.name = UBOOT_DM_CLK_IMX_FIXED_FACTOR,
.id = UCLASS_CLK,
.ops = &ccf_clk_fixed_factor_ops,
.flags = DM_FLAG_PRE_RELOC,
};

172
drivers/clk/clk-mux.c Normal file
View File

@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
*
* Simple multiplexer clock implementation
*/
/*
* U-Boot CCF porting node:
*
* The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux()
* version of CCF mux. It is used on e.g. imx6q to provide fixes (like
* imx_cscmr1_fixup) for broken HW.
*
* At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent
* clock.
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <linux/clk-provider.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
unsigned int val)
{
struct clk_mux *mux = to_clk_mux(clk);
int num_parents = mux->num_parents;
if (table) {
int i;
for (i = 0; i < num_parents; i++)
if (table[i] == val)
return i;
return -EINVAL;
}
if (val && (flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
if (val && (flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= num_parents)
return -EINVAL;
return val;
}
static u8 clk_mux_get_parent(struct clk *clk)
{
struct clk_mux *mux = to_clk_mux(clk);
u32 val;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
val = mux->io_mux_val;
#else
val = readl(mux->reg);
#endif
val >>= mux->shift;
val &= mux->mask;
return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
}
const struct clk_ops clk_mux_ops = {
.get_rate = clk_generic_get_rate,
};
struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table)
{
struct clk_mux *mux;
struct clk *clk;
u8 width = 0;
int ret;
if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
width = fls(mask) - ffs(mask) + 1;
if (width + shift > 16) {
pr_err("mux value exceeds LOWORD field\n");
return ERR_PTR(-EINVAL);
}
}
/* allocate the mux */
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
/* U-boot specific assignments */
mux->parent_names = parent_names;
mux->num_parents = num_parents;
/* struct clk_mux assignments */
mux->reg = reg;
mux->shift = shift;
mux->mask = mask;
mux->flags = clk_mux_flags;
mux->table = table;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
mux->io_mux_val = *(u32 *)reg;
#endif
clk = &mux->clk;
/*
* Read the current mux setup - so we assign correct parent.
*
* Changing parent would require changing internals of udevice struct
* for the corresponding clock (to do that define .set_parent() method.
*/
ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name,
parent_names[clk_mux_get_parent(clk)]);
if (ret) {
kfree(mux);
return ERR_PTR(ret);
}
return clk;
}
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table)
{
struct clk *clk;
clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags,
table);
if (IS_ERR(clk))
return ERR_CAST(clk);
return clk;
}
struct clk *clk_register_mux(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags)
{
u32 mask = BIT(width) - 1;
return clk_register_mux_table(dev, name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags,
NULL);
}
U_BOOT_DRIVER(ccf_clk_mux) = {
.name = UBOOT_DM_CLK_CCF_MUX,
.id = UCLASS_CLK,
.ops = &clk_mux_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -13,6 +13,7 @@
#include <dm/read.h>
#include <dt-structs.h>
#include <errno.h>
#include <linux/clk-provider.h>
static inline const struct clk_ops *clk_dev_ops(struct udevice *dev)
{
@@ -381,6 +382,43 @@ ulong clk_get_rate(struct clk *clk)
return ops->get_rate(clk);
}
struct clk *clk_get_parent(struct clk *clk)
{
struct udevice *pdev;
struct clk *pclk;
debug("%s(clk=%p)\n", __func__, clk);
pdev = dev_get_parent(clk->dev);
pclk = dev_get_clk_ptr(pdev);
if (!pclk)
return ERR_PTR(-ENODEV);
return pclk;
}
long long clk_get_parent_rate(struct clk *clk)
{
const struct clk_ops *ops;
struct clk *pclk;
debug("%s(clk=%p)\n", __func__, clk);
pclk = clk_get_parent(clk);
if (IS_ERR(pclk))
return -ENODEV;
ops = clk_dev_ops(pclk->dev);
if (!ops->get_rate)
return -ENOSYS;
/* Read the 'rate' if not already set or if proper flag set*/
if (!pclk->rate || pclk->flags & CLK_GET_RATE_NOCACHE)
pclk->rate = clk_get_rate(pclk);
return pclk->rate;
}
ulong clk_set_rate(struct clk *clk, ulong rate)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
@@ -455,6 +493,28 @@ int clk_disable_bulk(struct clk_bulk *bulk)
return 0;
}
int clk_get_by_id(ulong id, struct clk **clkp)
{
struct udevice *dev;
struct uclass *uc;
int ret;
ret = uclass_get(UCLASS_CLK, &uc);
if (ret)
return ret;
uclass_foreach_dev(dev, uc) {
struct clk *clk = dev_get_clk_ptr(dev);
if (clk && clk->id == id) {
*clkp = clk;
return 0;
}
}
return -ENOENT;
}
UCLASS_DRIVER(clk) = {
.id = UCLASS_CLK,
.name = "clk",

57
drivers/clk/clk.c Normal file
View File

@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <dm/uclass.h>
#include <dm/lists.h>
#include <dm/device-internal.h>
#include <clk.h>
int clk_register(struct clk *clk, const char *drv_name,
const char *name, const char *parent_name)
{
struct udevice *parent;
struct driver *drv;
int ret;
ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent);
if (ret)
printf("%s: UCLASS parent: 0x%p\n", __func__, parent);
debug("%s: name: %s parent: %s [0x%p]\n", __func__, name, parent->name,
parent);
drv = lists_driver_lookup_name(drv_name);
if (!drv) {
printf("%s: %s is not a valid driver name\n",
__func__, drv_name);
return -ENOENT;
}
ret = device_bind(parent, drv, name, NULL, -1, &clk->dev);
if (ret) {
printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name,
ret);
return ret;
}
/* Store back pointer to clk from udevice */
clk->dev->uclass_priv = clk;
return 0;
}
ulong clk_generic_get_rate(struct clk *clk)
{
return clk_get_parent_rate(clk);
}
const char *clk_hw_get_name(const struct clk *hw)
{
return hw->dev->name;
}

View File

@@ -24,9 +24,6 @@ static ulong clk_fixed_factor_get_rate(struct clk *clk)
uint64_t rate;
struct clk_fixed_factor *ff = to_clk_fixed_factor(clk->dev);
if (clk->id != 0)
return -EINVAL;
rate = clk_get_rate(&ff->parent);
if (IS_ERR_VALUE(rate))
return rate;

View File

@@ -8,6 +8,7 @@
#include <dm.h>
struct clk_fixed_rate {
struct clk clk;
unsigned long fixed_rate;
};
@@ -15,9 +16,6 @@ struct clk_fixed_rate {
static ulong clk_fixed_rate_get_rate(struct clk *clk)
{
if (clk->id != 0)
return -EINVAL;
return to_clk_fixed_rate(clk->dev)->fixed_rate;
}
@@ -27,10 +25,14 @@ const struct clk_ops clk_fixed_rate_ops = {
static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev)
{
struct clk *clk = &to_clk_fixed_rate(dev)->clk;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
to_clk_fixed_rate(dev)->fixed_rate =
dev_read_u32_default(dev, "clock-frequency", 0);
#endif
/* Make fixed rate clock accessible from higher level struct clk */
dev->uclass_priv = clk;
clk->dev = dev;
return 0;
}

View File

@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Common Clock Framework [CCF] driver for Sandbox
*/
#include <common.h>
#include <dm.h>
#include <clk.h>
#include <asm/clk.h>
#include <clk-uclass.h>
#include <linux/clk-provider.h>
#include <sandbox-clk.h>
/*
* Sandbox implementation of CCF primitives necessary for clk-uclass testing
*
* --- Sandbox PLLv3 ---
*/
struct clk_pllv3 {
struct clk clk;
u32 div_mask;
u32 div_shift;
};
static ulong clk_pllv3_get_rate(struct clk *clk)
{
unsigned long parent_rate = clk_get_parent_rate(clk);
return parent_rate * 24;
}
static const struct clk_ops clk_pllv3_generic_ops = {
.get_rate = clk_pllv3_get_rate,
};
struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name,
const char *parent_name, void __iomem *base,
u32 div_mask)
{
struct clk_pllv3 *pll;
struct clk *clk;
char *drv_name = "sandbox_clk_pllv3";
int ret;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
pll->div_mask = div_mask;
clk = &pll->clk;
ret = clk_register(clk, drv_name, name, parent_name);
if (ret) {
kfree(pll);
return ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(sandbox_clk_pll_generic) = {
.name = "sandbox_clk_pllv3",
.id = UCLASS_CLK,
.ops = &clk_pllv3_generic_ops,
};
/* --- Sandbox PLLv3 --- */
/* --- Sandbox Gate --- */
struct clk_gate2 {
struct clk clk;
bool state;
};
#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
static int clk_gate2_enable(struct clk *clk)
{
struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
gate->state = 1;
return 0;
}
static int clk_gate2_disable(struct clk *clk)
{
struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
gate->state = 0;
return 0;
}
static const struct clk_ops clk_gate2_ops = {
.enable = clk_gate2_enable,
.disable = clk_gate2_disable,
.get_rate = clk_generic_get_rate,
};
struct clk *sandbox_clk_register_gate2(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
u8 bit_idx, u8 cgr_val,
u8 clk_gate2_flags)
{
struct clk_gate2 *gate;
struct clk *clk;
int ret;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->state = 0;
clk = &gate->clk;
ret = clk_register(clk, "sandbox_clk_gate2", name, parent_name);
if (ret) {
kfree(gate);
return ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(sandbox_clk_gate2) = {
.name = "sandbox_clk_gate2",
.id = UCLASS_CLK,
.ops = &clk_gate2_ops,
};
/* --- Sandbox Gate --- */
/* The CCF core driver itself */
static const struct udevice_id sandbox_clk_ccf_test_ids[] = {
{ .compatible = "sandbox,clk-ccf" },
{ }
};
static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", };
static int sandbox_clk_ccf_probe(struct udevice *dev)
{
void *base = NULL;
u32 reg;
clk_dm(SANDBOX_CLK_PLL3,
sandbox_clk_pllv3(SANDBOX_PLLV3_USB, "pll3_usb_otg", "osc",
base + 0x10, 0x3));
clk_dm(SANDBOX_CLK_PLL3_60M,
sandbox_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
clk_dm(SANDBOX_CLK_PLL3_80M,
sandbox_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6));
/* The HW adds +1 to the divider value (2+1) is the divider */
reg = (2 << 19);
clk_dm(SANDBOX_CLK_ECSPI_ROOT,
sandbox_clk_divider("ecspi_root", "pll3_60m", &reg, 19, 6));
clk_dm(SANDBOX_CLK_ECSPI1,
sandbox_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
/* Select 'pll3_60m' */
reg = 0;
clk_dm(SANDBOX_CLK_USDHC1_SEL,
sandbox_clk_mux("usdhc1_sel", &reg, 16, 1, usdhc_sels,
ARRAY_SIZE(usdhc_sels)));
/* Select 'pll3_80m' */
reg = BIT(17);
clk_dm(SANDBOX_CLK_USDHC2_SEL,
sandbox_clk_mux("usdhc2_sel", &reg, 17, 1, usdhc_sels,
ARRAY_SIZE(usdhc_sels)));
return 0;
}
U_BOOT_DRIVER(sandbox_clk_ccf) = {
.name = "sandbox_clk_ccf",
.id = UCLASS_CLK,
.probe = sandbox_clk_ccf_probe,
.of_match = sandbox_clk_ccf_test_ids,
};

View File

@@ -1,3 +1,19 @@
config SPL_CLK_IMX6Q
bool "SPL clock support for i.MX6Q"
depends on ARCH_MX6 && SPL
select SPL_CLK
select SPL_CLK_CCF
help
This enables SPL DM/DTS support for clock driver in i.MX6Q platforms.
config CLK_IMX6Q
bool "Clock support for i.MX6Q"
depends on ARCH_MX6
select CLK
select CLK_CCF
help
This enables DM/DTS support for clock driver in i.MX6Q platforms.
config CLK_IMX8
bool "Clock support for i.MX8"
depends on ARCH_IMX8

View File

@@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o
obj-$(CONFIG_$(SPL_TPL_)CLK_IMX6Q) += clk-imx6q.o
obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
ifdef CONFIG_CLK_IMX8

103
drivers/clk/imx/clk-gate2.c Normal file
View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Gated clock implementation
*
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <linux/clk-provider.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2"
struct clk_gate2 {
struct clk clk;
void __iomem *reg;
u8 bit_idx;
u8 cgr_val;
u8 flags;
};
#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
static int clk_gate2_enable(struct clk *clk)
{
struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
u32 reg;
reg = readl(gate->reg);
reg &= ~(3 << gate->bit_idx);
reg |= gate->cgr_val << gate->bit_idx;
writel(reg, gate->reg);
return 0;
}
static int clk_gate2_disable(struct clk *clk)
{
struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
u32 reg;
reg = readl(gate->reg);
reg &= ~(3 << gate->bit_idx);
writel(reg, gate->reg);
return 0;
}
static const struct clk_ops clk_gate2_ops = {
.enable = clk_gate2_enable,
.disable = clk_gate2_disable,
.get_rate = clk_generic_get_rate,
};
struct clk *clk_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
u8 clk_gate2_flags)
{
struct clk_gate2 *gate;
struct clk *clk;
int ret;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = reg;
gate->bit_idx = bit_idx;
gate->cgr_val = cgr_val;
gate->flags = clk_gate2_flags;
clk = &gate->clk;
ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name);
if (ret) {
kfree(gate);
return ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(clk_gate2) = {
.name = UBOOT_DM_CLK_IMX_GATE2,
.id = UCLASS_CLK,
.ops = &clk_gate2_ops,
.flags = DM_FLAG_PRE_RELOC,
};

179
drivers/clk/imx/clk-imx6q.c Normal file
View File

@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <dt-bindings/clock/imx6qdl-clock.h>
#include "clk.h"
static int imx6q_check_id(ulong id)
{
if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
printf("%s: Invalid clk ID #%lu\n", __func__, id);
return -EINVAL;
}
return 0;
}
static ulong imx6q_clk_get_rate(struct clk *clk)
{
struct clk *c;
int ret;
debug("%s(#%lu)\n", __func__, clk->id);
ret = imx6q_check_id(clk->id);
if (ret)
return ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
return clk_get_rate(c);
}
static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate)
{
debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
return rate;
}
static int __imx6q_clk_enable(struct clk *clk, bool enable)
{
struct clk *c;
int ret = 0;
debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
ret = imx6q_check_id(clk->id);
if (ret)
return ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
if (enable)
ret = clk_enable(c);
else
ret = clk_disable(c);
return ret;
}
static int imx6q_clk_disable(struct clk *clk)
{
return __imx6q_clk_enable(clk, 0);
}
static int imx6q_clk_enable(struct clk *clk)
{
return __imx6q_clk_enable(clk, 1);
}
static struct clk_ops imx6q_clk_ops = {
.set_rate = imx6q_clk_set_rate,
.get_rate = imx6q_clk_get_rate,
.enable = imx6q_clk_enable,
.disable = imx6q_clk_disable,
};
static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
static int imx6q_clk_probe(struct udevice *dev)
{
void *base;
/* Anatop clocks */
base = (void *)ANATOP_BASE_ADDR;
clk_dm(IMX6QDL_CLK_PLL2,
imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
base + 0x30, 0x1));
clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
base + 0x10, 0x3));
clk_dm(IMX6QDL_CLK_PLL3_60M,
imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
/* CCM clocks */
base = dev_read_addr_ptr(dev);
if (base == (void *)FDT_ADDR_T_NONE)
return -EINVAL;
clk_dm(IMX6QDL_CLK_USDHC1_SEL,
imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC2_SEL,
imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC3_SEL,
imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC4_SEL,
imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC1_PODF,
imx_clk_divider("usdhc1_podf", "usdhc1_sel",
base + 0x24, 11, 3));
clk_dm(IMX6QDL_CLK_USDHC2_PODF,
imx_clk_divider("usdhc2_podf", "usdhc2_sel",
base + 0x24, 16, 3));
clk_dm(IMX6QDL_CLK_USDHC3_PODF,
imx_clk_divider("usdhc3_podf", "usdhc3_sel",
base + 0x24, 19, 3));
clk_dm(IMX6QDL_CLK_USDHC4_PODF,
imx_clk_divider("usdhc4_podf", "usdhc4_sel",
base + 0x24, 22, 3));
clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
clk_dm(IMX6QDL_CLK_ECSPI1,
imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
clk_dm(IMX6QDL_CLK_ECSPI2,
imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
clk_dm(IMX6QDL_CLK_ECSPI3,
imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
clk_dm(IMX6QDL_CLK_ECSPI4,
imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
clk_dm(IMX6QDL_CLK_USDHC1,
imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
clk_dm(IMX6QDL_CLK_USDHC2,
imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
clk_dm(IMX6QDL_CLK_USDHC3,
imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
clk_dm(IMX6QDL_CLK_USDHC4,
imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
return 0;
}
static const struct udevice_id imx6q_clk_ids[] = {
{ .compatible = "fsl,imx6q-ccm" },
{ },
};
U_BOOT_DRIVER(imx6q_clk) = {
.name = "clk_imx6q",
.id = UCLASS_CLK,
.of_match = imx6q_clk_ids,
.ops = &imx6q_clk_ops,
.probe = imx6q_clk_probe,
.flags = DM_FLAG_PRE_RELOC,
};

90
drivers/clk/imx/clk-pfd.c Normal file
View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <linux/clk-provider.h>
#include <div64.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd"
struct clk_pfd {
struct clk clk;
void __iomem *reg;
u8 idx;
};
#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk)
#define SET 0x4
#define CLR 0x8
#define OTG 0xc
static unsigned long clk_pfd_recalc_rate(struct clk *clk)
{
struct clk_pfd *pfd =
to_clk_pfd(dev_get_clk_ptr(clk->dev));
unsigned long parent_rate = clk_get_parent_rate(clk);
u64 tmp = parent_rate;
u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
tmp *= 18;
do_div(tmp, frac);
return tmp;
}
static const struct clk_ops clk_pfd_ops = {
.get_rate = clk_pfd_recalc_rate,
};
struct clk *imx_clk_pfd(const char *name, const char *parent_name,
void __iomem *reg, u8 idx)
{
struct clk_pfd *pfd;
struct clk *clk;
int ret;
pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
if (!pfd)
return ERR_PTR(-ENOMEM);
pfd->reg = reg;
pfd->idx = idx;
/* register the clock */
clk = &pfd->clk;
ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, name, parent_name);
if (ret) {
kfree(pfd);
return ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(clk_pfd) = {
.name = UBOOT_DM_CLK_IMX_PFD,
.id = UCLASS_CLK,
.ops = &clk_pfd_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <dm/uclass.h>
#include <clk.h>
#include "clk.h"
#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3"
struct clk_pllv3 {
struct clk clk;
void __iomem *base;
u32 div_mask;
u32 div_shift;
};
#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
static ulong clk_pllv3_get_rate(struct clk *clk)
{
struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
unsigned long parent_rate = clk_get_parent_rate(clk);
u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
return (div == 1) ? parent_rate * 22 : parent_rate * 20;
}
static const struct clk_ops clk_pllv3_generic_ops = {
.get_rate = clk_pllv3_get_rate,
};
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
const char *parent_name, void __iomem *base,
u32 div_mask)
{
struct clk_pllv3 *pll;
struct clk *clk;
char *drv_name;
int ret;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
switch (type) {
case IMX_PLLV3_GENERIC:
case IMX_PLLV3_USB:
drv_name = UBOOT_DM_CLK_IMX_PLLV3;
break;
default:
kfree(pll);
return ERR_PTR(-ENOTSUPP);
}
pll->base = base;
pll->div_mask = div_mask;
clk = &pll->clk;
ret = clk_register(clk, drv_name, name, parent_name);
if (ret) {
kfree(pll);
return ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(clk_pllv3_generic) = {
.name = UBOOT_DM_CLK_IMX_PLLV3,
.id = UCLASS_CLK,
.ops = &clk_pllv3_generic_ops,
.flags = DM_FLAG_PRE_RELOC,
};

69
drivers/clk/imx/clk.h Normal file
View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#ifndef __MACH_IMX_CLK_H
#define __MACH_IMX_CLK_H
#include <linux/clk-provider.h>
enum imx_pllv3_type {
IMX_PLLV3_GENERIC,
IMX_PLLV3_SYS,
IMX_PLLV3_USB,
IMX_PLLV3_USB_VF610,
IMX_PLLV3_AV,
IMX_PLLV3_ENET,
IMX_PLLV3_ENET_IMX7,
IMX_PLLV3_SYS_VF610,
IMX_PLLV3_DDR_IMX7,
};
struct clk *clk_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
u8 clk_gate_flags);
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
const char *parent_name, void __iomem *base,
u32 div_mask);
static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
shift, 0x3, 0);
}
static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
{
return clk_register_fixed_factor(NULL, name, parent,
CLK_SET_RATE_PARENT, mult, div);
}
static inline struct clk *imx_clk_divider(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
{
return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
reg, shift, width, 0);
}
struct clk *imx_clk_pfd(const char *name, const char *parent_name,
void __iomem *reg, u8 idx);
struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents,
int num_parents, void (*fixup)(u32 *val));
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents,
int num_parents)
{
return clk_register_mux(NULL, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT, reg, shift,
width, 0);
}
#endif /* __MACH_IMX_CLK_H */

View File

@@ -31,7 +31,7 @@ struct mxc_bank_info {
};
#ifndef CONFIG_DM_GPIO
#define GPIO_TO_PORT(n) (n / 32)
#define GPIO_TO_PORT(n) ((n) / 32)
/* GPIO port description */
static unsigned long gpio_ports[] = {

View File

@@ -51,6 +51,7 @@ void mxs_gpio_init(void)
}
}
#if !CONFIG_IS_ENABLED(DM_GPIO)
int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
@@ -127,3 +128,150 @@ int name_to_gpio(const char *name)
return (bank << MXS_PAD_BANK_SHIFT) | (pin << MXS_PAD_PIN_SHIFT);
}
#else /* CONFIG_DM_GPIO */
#include <dm.h>
#include <asm/gpio.h>
#include <asm/arch/gpio.h>
#define MXS_MAX_GPIO_PER_BANK 32
DECLARE_GLOBAL_DATA_PTR;
/*
* According to i.MX28 Reference Manual:
* 'i.MX28 Applications Processor Reference Manual, Rev. 1, 2010'
* The i.MX28 has following number of GPIOs available:
* Bank 0: 0-28 -> 29 PINS
* Bank 1: 0-31 -> 32 PINS
* Bank 2: 0-27 -> 28 PINS
* Bank 3: 0-30 -> 31 PINS
* Bank 4: 0-20 -> 21 PINS
*/
struct mxs_gpio_priv {
unsigned int bank;
};
static int mxs_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DIN(priv->bank));
return (readl(&reg->reg) >> offset) & 1;
}
static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOUT(priv->bank));
if (value)
writel(BIT(offset), &reg->reg_set);
else
writel(BIT(offset), &reg->reg_clr);
return 0;
}
static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
writel(BIT(offset), &reg->reg_clr);
return 0;
}
static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
mxs_gpio_set_value(dev, offset, value);
writel(BIT(offset), &reg->reg_set);
return 0;
}
static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
bool is_output = !!(readl(&reg->reg) >> offset);
return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
}
static const struct dm_gpio_ops gpio_mxs_ops = {
.direction_input = mxs_gpio_direction_input,
.direction_output = mxs_gpio_direction_output,
.get_value = mxs_gpio_get_value,
.set_value = mxs_gpio_set_value,
.get_function = mxs_gpio_get_function,
};
static int mxs_gpio_probe(struct udevice *dev)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct fdtdec_phandle_args args;
int node = dev_of_offset(dev);
char name[16], *str;
fdt_addr_t addr;
int ret;
addr = devfdt_get_addr(dev);
if (addr == FDT_ADDR_T_NONE) {
printf("%s: No 'reg' property defined!\n", __func__);
return -EINVAL;
}
priv->bank = (unsigned int)addr;
snprintf(name, sizeof(name), "GPIO%d_", priv->bank);
str = strdup(name);
if (!str)
return -ENOMEM;
uc_priv->bank_name = str;
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
NULL, 3, 0, &args);
if (ret)
printf("%s: 'gpio-ranges' not defined - using default!\n",
__func__);
uc_priv->gpio_count = ret == 0 ? args.args[2] : MXS_MAX_GPIO_PER_BANK;
debug("%s: %s: %d pins\n", __func__, uc_priv->bank_name,
uc_priv->gpio_count);
return 0;
}
static const struct udevice_id mxs_gpio_ids[] = {
{ .compatible = "fsl,imx23-gpio" },
{ .compatible = "fsl,imx28-gpio" },
{ }
};
U_BOOT_DRIVER(gpio_mxs) = {
.name = "gpio_mxs",
.id = UCLASS_GPIO,
.ops = &gpio_mxs_ops,
.probe = mxs_gpio_probe,
.priv_auto_alloc_size = sizeof(struct mxs_gpio_priv),
.of_match = mxs_gpio_ids,
};
#endif /* CONFIG_DM_GPIO */

View File

@@ -273,6 +273,34 @@ int sc_misc_otp_fuse_read(sc_ipc_t ipc, u32 word, u32 *val)
return 0;
}
int sc_misc_get_temp(sc_ipc_t ipc, sc_rsrc_t resource, sc_misc_temp_t temp,
s16 *celsius, s8 *tenths)
{
struct udevice *dev = gd->arch.scu_dev;
int size = sizeof(struct sc_rpc_msg_s);
struct sc_rpc_msg_s msg;
int ret;
RPC_VER(&msg) = SC_RPC_VERSION;
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_TEMP;
RPC_U16(&msg, 0U) = (u16)resource;
RPC_U8(&msg, 2U) = (u8)temp;
RPC_SIZE(&msg) = 2U;
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
if (ret < 0)
return ret;
if (celsius)
*celsius = RPC_I16(&msg, 0U);
if (tenths)
*tenths = RPC_I8(&msg, 2U);
return 0;
}
/* RM */
sc_bool_t sc_rm_is_memreg_owned(sc_ipc_t ipc, sc_rm_mr_t mr)
{

View File

@@ -25,7 +25,7 @@
#include <asm/mach-imx/regs-bch.h>
#include <asm/mach-imx/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
#include "mxs_nand.h"
#include <mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4

View File

@@ -1,73 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* NXP GPMI NAND flash driver
*
* Copyright (C) 2018 Toradex
* Authors:
* Stefan Agner <stefan.agner@toradex.com>
*/
#include <linux/mtd/mtd.h>
#include <asm/cache.h>
#include <nand.h>
#include <asm/mach-imx/dma.h>
/**
* @gf_len: The length of Galois Field. (e.g., 13 or 14)
* @ecc_strength: A number that describes the strength of the ECC
* algorithm.
* @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
* the first chunk in the page includes both data and
* metadata, so it's a bit larger than this value.
* @ecc_chunk_count: The number of ECC chunks in the page,
* @block_mark_byte_offset: The byte offset in the ECC-based page view at
* which the underlying physical block mark appears.
* @block_mark_bit_offset: The bit offset into the ECC-based page view at
* which the underlying physical block mark appears.
*/
struct bch_geometry {
unsigned int gf_len;
unsigned int ecc_strength;
unsigned int ecc_chunk_size;
unsigned int ecc_chunk_count;
unsigned int block_mark_byte_offset;
unsigned int block_mark_bit_offset;
};
struct mxs_nand_info {
struct nand_chip chip;
struct udevice *dev;
unsigned int max_ecc_strength_supported;
bool use_minimum_ecc;
int cur_chip;
uint32_t cmd_queue_len;
uint32_t data_buf_size;
struct bch_geometry bch_geometry;
uint8_t *cmd_buf;
uint8_t *data_buf;
uint8_t *oob_buf;
uint8_t marking_block_bad;
uint8_t raw_oob_mode;
struct mxs_gpmi_regs *gpmi_regs;
struct mxs_bch_regs *bch_regs;
/* Functions with altered behaviour */
int (*hooked_read_oob)(struct mtd_info *mtd,
loff_t from, struct mtd_oob_ops *ops);
int (*hooked_write_oob)(struct mtd_info *mtd,
loff_t to, struct mtd_oob_ops *ops);
int (*hooked_block_markbad)(struct mtd_info *mtd,
loff_t ofs);
/* DMA descriptors */
struct mxs_dma_desc **desc;
uint32_t desc_index;
};
int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
int mxs_nand_init_spl(struct nand_chip *nand);
int mxs_nand_setup_ecc(struct mtd_info *mtd);

View File

@@ -15,7 +15,7 @@
#include <linux/ioport.h>
#include <linux/printk.h>
#include "mxs_nand.h"
#include <mxs_nand.h>
struct mxs_nand_dt_data {
unsigned int max_ecc_strength_supported;

View File

@@ -6,7 +6,7 @@
#include <common.h>
#include <nand.h>
#include <malloc.h>
#include "mxs_nand.h"
#include <mxs_nand.h>
static struct mtd_info *mtd;
static struct nand_chip nand_chip;

View File

@@ -235,7 +235,7 @@ config FEC_MXC_MDIO_BASE
config FEC_MXC
bool "FEC Ethernet controller"
depends on MX5 || MX6 || MX7 || IMX8 || VF610
depends on MX28 || MX5 || MX6 || MX7 || IMX8 || VF610
help
This driver supports the 10/100 Fast Ethernet controller for
NXP i.MX processors.

View File

@@ -1485,6 +1485,7 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id fecmxc_ids[] = {
{ .compatible = "fsl,imx28-fec" },
{ .compatible = "fsl,imx6q-fec" },
{ .compatible = "fsl,imx6sl-fec" },
{ .compatible = "fsl,imx6sx-fec" },

View File

@@ -89,6 +89,16 @@ config PINCTRL_IMX8M
only parses the 'fsl,pins' property and configure related
registers.
config PINCTRL_MXS
bool "NXP MXS pinctrl driver"
depends on ARCH_MX28 && PINCTRL_FULL
help
Say Y here to enable the i.MX mxs pinctrl driver
This option provides a simple pinctrl driver for i.MX mxs SoC
familiy, e.g. i.MX28. This feature depends on device tree
configuration.
config PINCTRL_VYBRID
bool "Vybrid (vf610) pinctrl driver"
depends on ARCH_VF610 && PINCTRL_FULL

View File

@@ -6,4 +6,5 @@ obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o
obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o
obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o
obj-$(CONFIG_PINCTRL_IMX8M) += pinctrl-imx8m.o
obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_VYBRID) += pinctrl-vf610.o

View File

@@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#include <common.h>
#include <linux/io.h>
#include <linux/err.h>
#include <dm.h>
#include <dm/pinctrl.h>
#include <dm/read.h>
#include "pinctrl-mxs.h"
DECLARE_GLOBAL_DATA_PTR;
struct mxs_pinctrl_priv {
void __iomem *base;
const struct mxs_regs *regs;
};
static unsigned long mxs_dt_node_to_map(struct udevice *conf)
{
unsigned long config = 0;
int ret;
u32 val;
ret = dev_read_u32(conf, "fsl,drive-strength", &val);
if (!ret)
config = val | MA_PRESENT;
ret = dev_read_u32(conf, "fsl,voltage", &val);
if (!ret)
config |= val << VOL_SHIFT | VOL_PRESENT;
ret = dev_read_u32(conf, "fsl,pull-up", &val);
if (!ret)
config |= val << PULL_SHIFT | PULL_PRESENT;
return config;
}
static int mxs_pinctrl_set_mux(struct udevice *dev, u32 val, int bank, int pin)
{
struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
int muxsel = MUXID_TO_MUXSEL(val), shift;
void __iomem *reg;
reg = iomux->base + iomux->regs->muxsel;
reg += bank * 0x20 + pin / 16 * 0x10;
shift = pin % 16 * 2;
mxs_pinctrl_rmwl(muxsel, 0x3, shift, reg);
debug(" mux %d,", muxsel);
return 0;
}
static int mxs_pinctrl_set_state(struct udevice *dev, struct udevice *conf)
{
struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
u32 *pin_data, val, ma, vol, pull;
int npins, size, i, ret;
unsigned long config;
debug("\n%s: set state: %s\n", __func__, conf->name);
size = dev_read_size(conf, "fsl,pinmux-ids");
if (size < 0)
return size;
if (!size || size % sizeof(int)) {
dev_err(dev, "Invalid fsl,pinmux-ids property in %s\n",
conf->name);
return -EINVAL;
}
npins = size / sizeof(int);
pin_data = devm_kzalloc(dev, size, 0);
if (!pin_data)
return -ENOMEM;
ret = dev_read_u32_array(conf, "fsl,pinmux-ids", pin_data, npins);
if (ret) {
dev_err(dev, "Error reading pin data.\n");
devm_kfree(dev, pin_data);
return -EINVAL;
}
config = mxs_dt_node_to_map(conf);
ma = CONFIG_TO_MA(config);
vol = CONFIG_TO_VOL(config);
pull = CONFIG_TO_PULL(config);
for (i = 0; i < npins; i++) {
int pinid, bank, pin, shift;
void __iomem *reg;
val = pin_data[i];
pinid = MUXID_TO_PINID(val);
bank = PINID_TO_BANK(pinid);
pin = PINID_TO_PIN(pinid);
debug("(val: 0x%x) pin %d,", val, pinid);
/* Setup pinmux */
mxs_pinctrl_set_mux(dev, val, bank, pin);
debug(" ma: %d, vol: %d, pull: %d\n", ma, vol, pull);
/* drive */
reg = iomux->base + iomux->regs->drive;
reg += bank * 0x40 + pin / 8 * 0x10;
/* mA */
if (config & MA_PRESENT) {
shift = pin % 8 * 4;
mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
}
/* vol */
if (config & VOL_PRESENT) {
shift = pin % 8 * 4 + 2;
if (vol)
writel(1 << shift, reg + SET);
else
writel(1 << shift, reg + CLR);
}
/* pull */
if (config & PULL_PRESENT) {
reg = iomux->base + iomux->regs->pull;
reg += bank * 0x10;
shift = pin;
if (pull)
writel(1 << shift, reg + SET);
else
writel(1 << shift, reg + CLR);
}
}
devm_kfree(dev, pin_data);
return 0;
}
static struct pinctrl_ops mxs_pinctrl_ops = {
.set_state = mxs_pinctrl_set_state,
};
static int mxs_pinctrl_probe(struct udevice *dev)
{
struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
iomux->base = dev_read_addr_ptr(dev);
iomux->regs = (struct mxs_regs *)dev_get_driver_data(dev);
return 0;
}
static const struct mxs_regs imx23_regs = {
.muxsel = 0x100,
.drive = 0x200,
.pull = 0x400,
};
static const struct mxs_regs imx28_regs = {
.muxsel = 0x100,
.drive = 0x300,
.pull = 0x600,
};
static const struct udevice_id mxs_pinctrl_match[] = {
{ .compatible = "fsl,imx23-pinctrl", .data = (ulong)&imx23_regs },
{ .compatible = "fsl,imx28-pinctrl", .data = (ulong)&imx28_regs },
{ /* sentinel */ }
};
U_BOOT_DRIVER(mxs_pinctrl) = {
.name = "mxs-pinctrl",
.id = UCLASS_PINCTRL,
.of_match = of_match_ptr(mxs_pinctrl_match),
.probe = mxs_pinctrl_probe,
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
.bind = dm_scan_fdt_dev,
#endif
.priv_auto_alloc_size = sizeof(struct mxs_pinctrl_priv),
.ops = &mxs_pinctrl_ops,
};

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2012 Freescale Semiconductor, Inc.
*/
#ifndef __PINCTRL_MXS_H
#define __PINCTRL_MXS_H
#include <dm/pinctrl.h>
#define SET 0x4
#define CLR 0x8
#define TOG 0xc
#define MXS_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin)
#define PINID(bank, pin) ((bank) * 32 + (pin))
/*
* pinmux-id bit field definitions
*
* bank: 15..12 (4)
* pin: 11..4 (8)
* muxsel: 3..0 (4)
*/
#define MUXID_TO_PINID(m) PINID((m) >> 12 & 0xf, (m) >> 4 & 0xff)
#define MUXID_TO_MUXSEL(m) ((m) & 0xf)
#define PINID_TO_BANK(p) ((p) >> 5)
#define PINID_TO_PIN(p) ((p) % 32)
/*
* pin config bit field definitions
*
* pull-up: 6..5 (2)
* voltage: 4..3 (2)
* mA: 2..0 (3)
*
* MSB of each field is presence bit for the config.
*/
#define PULL_PRESENT (1 << 6)
#define PULL_SHIFT 5
#define VOL_PRESENT (1 << 4)
#define VOL_SHIFT 3
#define MA_PRESENT (1 << 2)
#define MA_SHIFT 0
#define CONFIG_TO_PULL(c) ((c) >> PULL_SHIFT & 0x1)
#define CONFIG_TO_VOL(c) ((c) >> VOL_SHIFT & 0x1)
#define CONFIG_TO_MA(c) ((c) >> MA_SHIFT & 0x3)
struct mxs_regs {
u16 muxsel;
u16 drive;
u16 pull;
};
static inline void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift,
void __iomem *reg)
{
clrsetbits_le32(reg, mask << shift, value << shift);
}
#endif /* __PINCTRL_MXS_H */

View File

@@ -3,6 +3,8 @@
* Copyright 2018 NXP
*/
#define DEBUG
#include <common.h>
#include <errno.h>
#include <dm.h>
@@ -15,15 +17,15 @@ DECLARE_GLOBAL_DATA_PTR;
static const struct pmic_child_info pmic_children_info[] = {
/* buck */
{ .prefix = "b", .driver = BD71837_REGULATOR_DRIVER},
{ .prefix = "b", .driver = BD718XX_REGULATOR_DRIVER},
/* ldo */
{ .prefix = "l", .driver = BD71837_REGULATOR_DRIVER},
{ .prefix = "l", .driver = BD718XX_REGULATOR_DRIVER},
{ },
};
static int bd71837_reg_count(struct udevice *dev)
{
return BD71837_REG_NUM;
return BD718XX_MAX_REGISTER - 1;
}
static int bd71837_write(struct udevice *dev, uint reg, const uint8_t *buff,
@@ -54,7 +56,7 @@ static int bd71837_bind(struct udevice *dev)
regulators_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regulators_node)) {
debug("%s: %s regulators subnode not found!", __func__,
debug("%s: %s regulators subnode not found!\n", __func__,
dev->name);
return -ENXIO;
}
@@ -69,6 +71,24 @@ static int bd71837_bind(struct udevice *dev)
return 0;
}
static int bd718x7_probe(struct udevice *dev)
{
int ret;
uint8_t mask = BD718XX_REGLOCK_PWRSEQ | BD718XX_REGLOCK_VREG;
/* Unlock the PMIC regulator control before probing the children */
ret = pmic_clrsetbits(dev, BD718XX_REGLOCK, mask, 0);
if (ret) {
debug("%s: %s Failed to unlock regulator control\n", __func__,
dev->name);
return ret;
}
debug("%s: '%s' - BD718x7 PMIC registers unlocked\n", __func__,
dev->name);
return 0;
}
static struct dm_pmic_ops bd71837_ops = {
.reg_count = bd71837_reg_count,
.read = bd71837_read,
@@ -76,7 +96,8 @@ static struct dm_pmic_ops bd71837_ops = {
};
static const struct udevice_id bd71837_ids[] = {
{ .compatible = "rohm,bd71837", .data = 0x4b, },
{ .compatible = "rohm,bd71837", .data = ROHM_CHIP_TYPE_BD71837, },
{ .compatible = "rohm,bd71847", .data = ROHM_CHIP_TYPE_BD71847, },
{ }
};
@@ -85,5 +106,6 @@ U_BOOT_DRIVER(pmic_bd71837) = {
.id = UCLASS_PMIC,
.of_match = bd71837_ids,
.bind = bd71837_bind,
.probe = bd718x7_probe,
.ops = &bd71837_ops,
};

View File

@@ -43,6 +43,23 @@ config REGULATOR_AS3722
but does not yet support change voltages. Currently this must be
done using direct register writes to the PMIC.
config DM_REGULATOR_BD71837
bool "Enable Driver Model for ROHM BD71837/BD71847 regulators"
depends on DM_REGULATOR && DM_PMIC_BD71837
help
This config enables implementation of driver-model regulator uclass
features for regulators on ROHM BD71837 and BD71847 PMICs.
BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version
containing 6 bucks and 6 LDOs. The driver implements get/set api for
value and enable.
config SPL_DM_REGULATOR_BD71837
bool "Enable Driver Model for ROHM BD71837/BD71847 regulators in SPL"
depends on DM_REGULATOR_BD71837
help
This config enables implementation of driver-model regulator uclass
features for regulators on ROHM BD71837 and BD71847 in SPL.
config DM_REGULATOR_PFUZE100
bool "Enable Driver Model for REGULATOR PFUZE100"
depends on DM_REGULATOR && DM_PMIC_PFUZE100

View File

@@ -9,6 +9,7 @@ obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o
obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o

View File

@@ -0,0 +1,468 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 ROHM Semiconductors
*
* ROHM BD71837 regulator driver
*/
#include <common.h>
#include <dm.h>
#include <power/bd71837.h>
#include <power/pmic.h>
#include <power/regulator.h>
#define HW_STATE_CONTROL 0
#define DEBUG
/**
* struct bd71837_vrange - describe linear range of voltages
*
* @min_volt: smallest voltage in range
* @step: how much voltage changes at each selector step
* @min_sel: smallest selector in the range
* @max_sel: maximum selector in the range
* @rangeval: register value used to select this range if selectible
* ranges are supported
*/
struct bd71837_vrange {
unsigned int min_volt;
unsigned int step;
u8 min_sel;
u8 max_sel;
u8 rangeval;
};
/**
* struct bd71837_platdata - describe regulator control registers
*
* @name: name of the regulator. Used for matching the dt-entry
* @enable_reg: register address used to enable/disable regulator
* @enablemask: register mask used to enable/disable regulator
* @volt_reg: register address used to configure regulator voltage
* @volt_mask: register mask used to configure regulator voltage
* @ranges: pointer to ranges of regulator voltages and matching register
* values
* @numranges: number of voltage ranges pointed by ranges
* @rangemask: mask for selecting used ranges if multiple ranges are supported
* @sel_mask: bit to toggle in order to transfer the register control to SW
* @dvs: whether the voltage can be changed when regulator is enabled
*/
struct bd71837_platdata {
const char *name;
u8 enable_reg;
u8 enablemask;
u8 volt_reg;
u8 volt_mask;
struct bd71837_vrange *ranges;
unsigned int numranges;
u8 rangemask;
u8 sel_mask;
bool dvs;
};
#define BD_RANGE(_min, _vstep, _sel_low, _sel_hi, _range_sel) \
{ \
.min_volt = (_min), .step = (_vstep), .min_sel = (_sel_low), \
.max_sel = (_sel_hi), .rangeval = (_range_sel) \
}
#define BD_DATA(_name, enreg, enmask, vreg, vmask, _range, rmask, _dvs, sel) \
{ \
.name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \
.volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \
.numranges = ARRAY_SIZE(_range), .rangemask = (rmask), .dvs = (_dvs), \
.sel_mask = (sel) \
}
static struct bd71837_vrange dvs_buck_vranges[] = {
BD_RANGE(700000, 10000, 0, 0x3c, 0),
BD_RANGE(1300000, 0, 0x3d, 0x3f, 0),
};
static struct bd71837_vrange bd71847_buck3_vranges[] = {
BD_RANGE(700000, 100000, 0x00, 0x03, 0),
BD_RANGE(1050000, 50000, 0x04, 0x05, 0),
BD_RANGE(1200000, 150000, 0x06, 0x07, 0),
BD_RANGE(550000, 50000, 0x0, 0x7, 0x40),
BD_RANGE(675000, 100000, 0x0, 0x3, 0x80),
BD_RANGE(1025000, 50000, 0x4, 0x5, 0x80),
BD_RANGE(1175000, 150000, 0x6, 0x7, 0x80),
};
static struct bd71837_vrange bd71847_buck4_vranges[] = {
BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
BD_RANGE(2600000, 100000, 0x00, 0x03, 40),
};
static struct bd71837_vrange bd71837_buck5_vranges[] = {
BD_RANGE(700000, 100000, 0, 0x3, 0),
BD_RANGE(1050000, 50000, 0x04, 0x05, 0),
BD_RANGE(1200000, 150000, 0x06, 0x07, 0),
BD_RANGE(675000, 100000, 0x0, 0x3, 0x80),
BD_RANGE(1025000, 50000, 0x04, 0x05, 0x80),
BD_RANGE(1175000, 150000, 0x06, 0x07, 0x80),
};
static struct bd71837_vrange bd71837_buck6_vranges[] = {
BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
};
static struct bd71837_vrange nodvs_buck3_vranges[] = {
BD_RANGE(1605000, 90000, 0, 1, 0),
BD_RANGE(1755000, 45000, 2, 4, 0),
BD_RANGE(1905000, 45000, 5, 7, 0),
};
static struct bd71837_vrange nodvs_buck4_vranges[] = {
BD_RANGE(800000, 10000, 0x00, 0x3C, 0),
};
static struct bd71837_vrange ldo1_vranges[] = {
BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
BD_RANGE(1600000, 100000, 0x00, 0x03, 0x20),
};
static struct bd71837_vrange ldo2_vranges[] = {
BD_RANGE(900000, 0, 0, 0, 0),
BD_RANGE(800000, 0, 1, 1, 0),
};
static struct bd71837_vrange ldo3_vranges[] = {
BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
};
static struct bd71837_vrange ldo4_vranges[] = {
BD_RANGE(900000, 100000, 0x00, 0x09, 0),
};
static struct bd71837_vrange bd71837_ldo5_vranges[] = {
BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
};
static struct bd71837_vrange bd71847_ldo5_vranges[] = {
BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
BD_RANGE(800000, 100000, 0x00, 0x0f, 0x20),
};
static struct bd71837_vrange ldo6_vranges[] = {
BD_RANGE(900000, 100000, 0x00, 0x09, 0),
};
static struct bd71837_vrange ldo7_vranges[] = {
BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
};
/*
* We use enable mask 'HW_STATE_CONTROL' to indicate that this regulator
* must not be enabled or disabled by SW. The typical use-case for BD71837
* is powering NXP i.MX8. In this use-case we (for now) only allow control
* for BUCK3 and BUCK4 which are not boot critical.
*/
static struct bd71837_platdata bd71837_reg_data[] = {
/* Bucks 1-4 which support dynamic voltage scaling */
BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL,
BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL,
BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
BD_DATA("BUCK3", BD71837_BUCK3_CTRL, BD718XX_BUCK_EN,
BD71837_BUCK3_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
BD_DATA("BUCK4", BD71837_BUCK4_CTRL, BD718XX_BUCK_EN,
BD71837_BUCK4_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
/* Bucks 5-8 which do not support dynamic voltage scaling */
BD_DATA("BUCK5", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK,
bd71837_buck5_vranges, 0x80, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK6", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK,
bd71837_buck6_vranges, 0, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK7", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK,
nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK8", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK,
nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL),
/* LDOs */
BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT,
BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL),
BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT,
BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT,
BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT,
BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT,
BD71837_LDO5_MASK, bd71837_ldo5_vranges, 0, false,
BD718XX_LDO_SEL),
BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT,
BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO7", BD71837_LDO7_VOLT, HW_STATE_CONTROL, BD71837_LDO7_VOLT,
BD71837_LDO7_MASK, ldo7_vranges, 0, false, BD718XX_LDO_SEL),
};
static struct bd71837_platdata bd71847_reg_data[] = {
/* Bucks 1 and 2 which support dynamic voltage scaling */
BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL,
BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL,
BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
true, BD718XX_BUCK_SEL),
/* Bucks 3-6 which do not support dynamic voltage scaling */
BD_DATA("BUCK3", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK,
bd71847_buck3_vranges, 0xc0, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK4", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK,
bd71847_buck4_vranges, 0x40, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK5", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK,
nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL),
BD_DATA("BUCK6", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK,
nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL),
/* LDOs */
BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT,
BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL),
BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT,
BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT,
BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT,
BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL),
BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT,
BD71847_LDO5_MASK, bd71847_ldo5_vranges, 0x20, false,
BD718XX_LDO_SEL),
BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT,
BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL),
};
static int vrange_find_value(struct bd71837_vrange *r, unsigned int sel,
unsigned int *val)
{
if (!val || sel < r->min_sel || sel > r->max_sel)
return -EINVAL;
*val = r->min_volt + r->step * (sel - r->min_sel);
return 0;
}
static int vrange_find_selector(struct bd71837_vrange *r, int val,
unsigned int *sel)
{
int ret = -EINVAL;
int num_vals = r->max_sel - r->min_sel + 1;
if (val >= r->min_volt &&
val <= r->min_volt + r->step * (num_vals - 1)) {
if (r->step) {
*sel = r->min_sel + ((val - r->min_volt) / r->step);
ret = 0;
} else {
*sel = r->min_sel;
ret = 0;
}
}
return ret;
}
static int bd71837_get_enable(struct udevice *dev)
{
int val;
struct bd71837_platdata *plat = dev_get_platdata(dev);
/*
* boot critical regulators on bd71837 must not be controlled by sw
* due to the 'feature' which leaves power rails down if bd71837 is
* reseted to snvs state. hence we can't get the state here.
*
* if we are alive it means we probably are on run state and
* if the regulator can't be controlled we can assume it is
* enabled.
*/
if (plat->enablemask == HW_STATE_CONTROL)
return 1;
val = pmic_reg_read(dev->parent, plat->enable_reg);
if (val < 0)
return val;
return (val & plat->enablemask);
}
static int bd71837_set_enable(struct udevice *dev, bool enable)
{
int val = 0;
struct bd71837_platdata *plat = dev_get_platdata(dev);
/*
* boot critical regulators on bd71837 must not be controlled by sw
* due to the 'feature' which leaves power rails down if bd71837 is
* reseted to snvs state. Hence we can't set the state here.
*/
if (plat->enablemask == HW_STATE_CONTROL)
return -EINVAL;
if (enable)
val = plat->enablemask;
return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask,
val);
}
static int bd71837_set_value(struct udevice *dev, int uvolt)
{
unsigned int sel;
unsigned int range;
int i;
int found = 0;
struct bd71837_platdata *plat = dev_get_platdata(dev);
/*
* An under/overshooting may occur if voltage is changed for other
* regulators but buck 1,2,3 or 4 when regulator is enabled. Prevent
* change to protect the HW
*/
if (!plat->dvs)
if (bd71837_get_enable(dev)) {
pr_err("Only DVS bucks can be changed when enabled\n");
return -EINVAL;
}
for (i = 0; i < plat->numranges; i++) {
struct bd71837_vrange *r = &plat->ranges[i];
found = !vrange_find_selector(r, uvolt, &sel);
if (found) {
unsigned int tmp;
/*
* We require exactly the requested value to be
* supported - this can be changed later if needed
*/
range = r->rangeval;
found = !vrange_find_value(r, sel, &tmp);
if (found && tmp == uvolt)
break;
found = 0;
}
}
if (!found)
return -EINVAL;
sel <<= ffs(plat->volt_mask) - 1;
if (plat->rangemask)
sel |= range;
return pmic_clrsetbits(dev->parent, plat->volt_reg, plat->volt_mask |
plat->rangemask, sel);
}
static int bd71837_get_value(struct udevice *dev)
{
unsigned int reg, range;
unsigned int tmp;
struct bd71837_platdata *plat = dev_get_platdata(dev);
int i;
reg = pmic_reg_read(dev->parent, plat->volt_reg);
if (((int)reg) < 0)
return reg;
range = reg & plat->rangemask;
reg &= plat->volt_mask;
reg >>= ffs(plat->volt_mask) - 1;
for (i = 0; i < plat->numranges; i++) {
struct bd71837_vrange *r = &plat->ranges[i];
if (plat->rangemask && ((plat->rangemask & range) !=
r->rangeval))
continue;
if (!vrange_find_value(r, reg, &tmp))
return tmp;
}
pr_err("Unknown voltage value read from pmic\n");
return -EINVAL;
}
static int bd71837_regulator_probe(struct udevice *dev)
{
struct bd71837_platdata *plat = dev_get_platdata(dev);
int i, ret;
struct dm_regulator_uclass_platdata *uc_pdata;
int type;
struct bd71837_platdata *init_data;
int data_amnt;
type = dev_get_driver_data(dev_get_parent(dev));
switch (type) {
case ROHM_CHIP_TYPE_BD71837:
init_data = bd71837_reg_data;
data_amnt = ARRAY_SIZE(bd71837_reg_data);
break;
case ROHM_CHIP_TYPE_BD71847:
init_data = bd71847_reg_data;
data_amnt = ARRAY_SIZE(bd71847_reg_data);
break;
default:
debug("Unknown PMIC type\n");
init_data = NULL;
data_amnt = 0;
break;
}
for (i = 0; i < data_amnt; i++) {
if (!strcmp(dev->name, init_data[i].name)) {
*plat = init_data[i];
if (plat->enablemask != HW_STATE_CONTROL) {
/*
* Take the regulator under SW control. Ensure
* the initial state matches dt flags and then
* write the SEL bit
*/
uc_pdata = dev_get_uclass_platdata(dev);
ret = bd71837_set_enable(dev,
!!(uc_pdata->boot_on ||
uc_pdata->always_on));
if (ret)
return ret;
return pmic_clrsetbits(dev->parent,
plat->enable_reg,
plat->sel_mask,
plat->sel_mask);
}
return 0;
}
}
pr_err("Unknown regulator '%s'\n", dev->name);
return -ENOENT;
}
static const struct dm_regulator_ops bd71837_regulator_ops = {
.get_value = bd71837_get_value,
.set_value = bd71837_set_value,
.get_enable = bd71837_get_enable,
.set_enable = bd71837_set_enable,
};
U_BOOT_DRIVER(bd71837_regulator) = {
.name = BD718XX_REGULATOR_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &bd71837_regulator_ops,
.probe = bd71837_regulator_probe,
.platdata_auto_alloc_size = sizeof(struct bd71837_platdata),
};

View File

@@ -18,6 +18,11 @@ config PWM_EXYNOS
used. It provides 5 channels which can be independently
programmed. Channel 4 (the last) is normally used as a timer.
config PWM_IMX
bool "Enable support for i.MX27 and later PWM"
help
This PWM is found i.MX27 and later i.MX SoCs.
config PWM_ROCKCHIP
bool "Enable support for the Rockchip PWM"
depends on DM_PWM

View File

@@ -8,6 +8,7 @@
#include <common.h>
#include <div64.h>
#include <dm.h>
#include <pwm.h>
#include <asm/arch/imx-regs.h>
#include <asm/io.h>
@@ -24,18 +25,12 @@ int pwm_init(int pwm_id, int div, int invert)
return 0;
}
int pwm_config(int pwm_id, int duty_ns, int period_ns)
int pwm_config_internal(struct pwm_regs *pwm, unsigned long period_cycles,
unsigned long duty_cycles, unsigned long prescale)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
unsigned long period_cycles, duty_cycles, prescale;
u32 cr;
if (!pwm)
return -1;
pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
&prescale);
writel(0, &pwm->ir);
cr = PWMCR_PRESCALER(prescale) |
PWMCR_DOZEEN | PWMCR_WAITEN |
PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH;
@@ -48,6 +43,20 @@ int pwm_config(int pwm_id, int duty_ns, int period_ns)
return 0;
}
int pwm_config(int pwm_id, int duty_ns, int period_ns)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
unsigned long period_cycles, duty_cycles, prescale;
if (!pwm)
return -1;
pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
&prescale);
return pwm_config_internal(pwm, period_cycles, duty_cycles, prescale);
}
int pwm_enable(int pwm_id)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
@@ -68,3 +77,86 @@ void pwm_disable(int pwm_id)
clrbits_le32(&pwm->cr, PWMCR_EN);
}
#if defined(CONFIG_DM_PWM)
struct imx_pwm_priv {
struct pwm_regs *regs;
bool invert;
};
static int imx_pwm_set_invert(struct udevice *dev, uint channel,
bool polarity)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
debug("%s: polarity=%u\n", __func__, polarity);
priv->invert = polarity;
return 0;
}
static int imx_pwm_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
struct pwm_regs *regs = priv->regs;
unsigned long period_cycles, duty_cycles, prescale;
debug("%s: Config '%s' channel: %d\n", __func__, dev->name, channel);
pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
&prescale);
return pwm_config_internal(regs, period_cycles, duty_cycles, prescale);
};
static int imx_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
struct pwm_regs *regs = priv->regs;
debug("%s: Enable '%s' state: %d\n", __func__, dev->name, enable);
if (enable)
setbits_le32(&regs->cr, PWMCR_EN);
else
clrbits_le32(&regs->cr, PWMCR_EN);
return 0;
};
static int imx_pwm_ofdata_to_platdata(struct udevice *dev)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
priv->regs = (struct pwm_regs *)devfdt_get_addr(dev);
return 0;
}
static int imx_pwm_probe(struct udevice *dev)
{
return 0;
}
static const struct pwm_ops imx_pwm_ops = {
.set_invert = imx_pwm_set_invert,
.set_config = imx_pwm_set_config,
.set_enable = imx_pwm_set_enable,
};
static const struct udevice_id imx_pwm_ids[] = {
{ .compatible = "fsl,imx27-pwm" },
{ }
};
U_BOOT_DRIVER(imx_pwm) = {
.name = "imx_pwm",
.id = UCLASS_PWM,
.of_match = imx_pwm_ids,
.ops = &imx_pwm_ops,
.ofdata_to_platdata = imx_pwm_ofdata_to_platdata,
.probe = imx_pwm_probe,
.priv_auto_alloc_size = sizeof(struct imx_pwm_priv),
};
#endif

View File

@@ -342,6 +342,8 @@ static int mxc_serial_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id mxc_serial_ids[] = {
{ .compatible = "fsl,imx21-uart" },
{ .compatible = "fsl,imx53-uart" },
{ .compatible = "fsl,imx6sx-uart" },
{ .compatible = "fsl,imx6ul-uart" },
{ .compatible = "fsl,imx7d-uart" },
@@ -360,9 +362,7 @@ U_BOOT_DRIVER(serial_mxc) = {
#endif
.probe = mxc_serial_probe,
.ops = &mxc_serial_ops,
#if !CONFIG_IS_ENABLED(OF_CONTROL)
.flags = DM_FLAG_PRE_RELOC,
#endif
};
#endif

View File

@@ -2,6 +2,9 @@
/*
* Freescale i.MX28 SPI driver
*
* Copyright (C) 2019 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
@@ -27,89 +30,6 @@
#define MXSSSP_SMALL_TRANSFER 512
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
uint32_t mode;
struct mxs_ssp_regs *regs;
};
static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
{
return container_of(slave, struct mxs_spi_slave, slave);
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* MXS SPI: 4 ports and 3 chip selects maximum */
if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
return 0;
else
return 1;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct mxs_spi_slave *mxs_slave;
if (!spi_cs_is_valid(bus, cs)) {
printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
return NULL;
}
mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
if (!mxs_slave)
return NULL;
if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
goto err_init;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
return &mxs_slave->slave;
err_init:
free(mxs_slave);
return NULL;
}
void spi_free_slave(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
free(mxs_slave);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
uint32_t reg = 0;
mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) |
SSP_CTRL0_BUS_WIDTH_ONE_BIT,
&ssp_regs->hw_ssp_ctrl0);
reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
writel(reg, &ssp_regs->hw_ssp_ctrl1);
writel(0, &ssp_regs->hw_ssp_cmd0);
mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
@@ -122,10 +42,50 @@ static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
}
#if !CONFIG_IS_ENABLED(DM_SPI)
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
uint32_t mode;
struct mxs_ssp_regs *regs;
};
static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
{
return container_of(slave, struct mxs_spi_slave, slave);
}
#else
#include <dm.h>
#include <errno.h>
struct mxs_spi_platdata {
s32 frequency; /* Default clock frequency, -1 for none */
fdt_addr_t base; /* SPI IP block base address */
int num_cs; /* Number of CSes supported */
int dma_id; /* ID of the DMA channel */
int clk_id; /* ID of the SSP clock */
};
struct mxs_spi_priv {
struct mxs_ssp_regs *regs;
unsigned int dma_channel;
unsigned int max_freq;
unsigned int clk_id;
unsigned int mode;
};
#endif
#if !CONFIG_IS_ENABLED(DM_SPI)
static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
struct mxs_ssp_regs *ssp_regs = slave->regs;
#else
static int mxs_spi_xfer_pio(struct mxs_spi_priv *priv,
char *data, int length, int write,
unsigned long flags)
{
struct mxs_ssp_regs *ssp_regs = priv->regs;
#endif
if (flags & SPI_XFER_BEGIN)
mxs_spi_start_xfer(ssp_regs);
@@ -181,12 +141,19 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
return 0;
}
#if !CONFIG_IS_ENABLED(DM_SPI)
static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
struct mxs_ssp_regs *ssp_regs = slave->regs;
#else
static int mxs_spi_xfer_dma(struct mxs_spi_priv *priv,
char *data, int length, int write,
unsigned long flags)
{ struct mxs_ssp_regs *ssp_regs = priv->regs;
#endif
const int xfer_max_sz = 0xff00;
const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1;
struct mxs_ssp_regs *ssp_regs = slave->regs;
struct mxs_dma_desc *dp;
uint32_t ctrl0;
uint32_t cache_data_count;
@@ -225,7 +192,11 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
/* Invalidate the area, so no writeback into the RAM races with DMA */
invalidate_dcache_range(dstart, dstart + cache_data_count);
#if !CONFIG_IS_ENABLED(DM_SPI)
dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
#else
dmach = priv->dma_channel;
#endif
dp = desc;
while (length) {
@@ -302,11 +273,20 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
return ret;
}
#if !CONFIG_IS_ENABLED(DM_SPI)
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
#else
int mxs_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct mxs_spi_priv *priv = dev_get_priv(bus);
struct mxs_ssp_regs *ssp_regs = priv->regs;
#endif
int len = bitlen / 8;
char dummy;
int write = 0;
@@ -350,9 +330,263 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
#if !CONFIG_IS_ENABLED(DM_SPI)
return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
#else
return mxs_spi_xfer_pio(priv, data, len, write, flags);
#endif
} else {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
#if !CONFIG_IS_ENABLED(DM_SPI)
return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
#else
return mxs_spi_xfer_dma(priv, data, len, write, flags);
#endif
}
}
#if !CONFIG_IS_ENABLED(DM_SPI)
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* MXS SPI: 4 ports and 3 chip selects maximum */
if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
return 0;
else
return 1;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct mxs_spi_slave *mxs_slave;
if (!spi_cs_is_valid(bus, cs)) {
printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
return NULL;
}
mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
if (!mxs_slave)
return NULL;
if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
goto err_init;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
return &mxs_slave->slave;
err_init:
free(mxs_slave);
return NULL;
}
void spi_free_slave(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
free(mxs_slave);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
u32 reg = 0;
mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) |
SSP_CTRL0_BUS_WIDTH_ONE_BIT,
&ssp_regs->hw_ssp_ctrl0);
reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
writel(reg, &ssp_regs->hw_ssp_ctrl1);
writel(0, &ssp_regs->hw_ssp_cmd0);
mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
#else /* CONFIG_DM_SPI */
/* Base numbers of i.MX2[38] clk for ssp0 IP block */
#define MXS_SSP_IMX23_CLKID_SSP0 33
#define MXS_SSP_IMX28_CLKID_SSP0 46
static int mxs_spi_probe(struct udevice *bus)
{
struct mxs_spi_platdata *plat = dev_get_platdata(bus);
struct mxs_spi_priv *priv = dev_get_priv(bus);
int ret;
debug("%s: probe\n", __func__);
priv->regs = (struct mxs_ssp_regs *)plat->base;
priv->max_freq = plat->frequency;
priv->dma_channel = plat->dma_id;
priv->clk_id = plat->clk_id;
ret = mxs_dma_init_channel(priv->dma_channel);
if (ret) {
printf("%s: DMA init channel error %d\n", __func__, ret);
return ret;
}
return 0;
}
static int mxs_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct mxs_spi_priv *priv = dev_get_priv(bus);
struct mxs_ssp_regs *ssp_regs = priv->regs;
int cs = spi_chip_select(dev);
/*
* i.MX28 supports up to 3 CS (SSn0, SSn1, SSn2)
* To set them it uses following tuple (WAIT_FOR_IRQ,WAIT_FOR_CMD),
* where:
*
* WAIT_FOR_IRQ is bit 21 of HW_SSP_CTRL0
* WAIT_FOR_CMD is bit 20 (#defined as MXS_SSP_CHIPSELECT_SHIFT here) of
* HW_SSP_CTRL0
* SSn0 b00
* SSn1 b01
* SSn2 b10 (which require setting WAIT_FOR_IRQ)
*
* However, for now i.MX28 SPI driver will support up till 2 CSes
* (SSn0, and SSn1).
*/
/* Ungate SSP clock and set active CS */
clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0,
BIT(MXS_SSP_CHIPSELECT_SHIFT) |
SSP_CTRL0_CLKGATE, (cs << MXS_SSP_CHIPSELECT_SHIFT));
return 0;
}
static int mxs_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct mxs_spi_priv *priv = dev_get_priv(bus);
struct mxs_ssp_regs *ssp_regs = priv->regs;
/* Gate SSP clock */
setbits_le32(&ssp_regs->hw_ssp_ctrl0, SSP_CTRL0_CLKGATE);
return 0;
}
static int mxs_spi_set_speed(struct udevice *bus, uint speed)
{
struct mxs_spi_priv *priv = dev_get_priv(bus);
#ifdef CONFIG_MX28
int clkid = priv->clk_id - MXS_SSP_IMX28_CLKID_SSP0;
#else /* CONFIG_MX23 */
int clkid = priv->clk_id - MXS_SSP_IMX23_CLKID_SSP0;
#endif
if (speed > priv->max_freq)
speed = priv->max_freq;
debug("%s speed: %u [Hz] clkid: %d\n", __func__, speed, clkid);
mxs_set_ssp_busclock(clkid, speed / 1000);
return 0;
}
static int mxs_spi_set_mode(struct udevice *bus, uint mode)
{
struct mxs_spi_priv *priv = dev_get_priv(bus);
struct mxs_ssp_regs *ssp_regs = priv->regs;
u32 reg;
priv->mode = mode;
debug("%s: mode 0x%x\n", __func__, mode);
reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
reg |= (priv->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
reg |= (priv->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
writel(reg, &ssp_regs->hw_ssp_ctrl1);
/* Single bit SPI support */
writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0);
return 0;
}
static const struct dm_spi_ops mxs_spi_ops = {
.claim_bus = mxs_spi_claim_bus,
.release_bus = mxs_spi_release_bus,
.xfer = mxs_spi_xfer,
.set_speed = mxs_spi_set_speed,
.set_mode = mxs_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
static int mxs_ofdata_to_platdata(struct udevice *bus)
{
struct mxs_spi_platdata *plat = bus->platdata;
u32 prop[2];
int ret;
plat->base = dev_read_addr(bus);
plat->frequency =
dev_read_u32_default(bus, "spi-max-frequency", 40000000);
plat->num_cs = dev_read_u32_default(bus, "num-cs", 2);
ret = dev_read_u32_array(bus, "dmas", prop, ARRAY_SIZE(prop));
if (ret) {
printf("%s: Reading 'dmas' property failed!\n", __func__);
return ret;
}
plat->dma_id = prop[1];
ret = dev_read_u32_array(bus, "clocks", prop, ARRAY_SIZE(prop));
if (ret) {
printf("%s: Reading 'clocks' property failed!\n", __func__);
return ret;
}
plat->clk_id = prop[1];
debug("%s: base=0x%x, max-frequency=%d num-cs=%d dma_id=%d clk_id=%d\n",
__func__, (uint)plat->base, plat->frequency, plat->num_cs,
plat->dma_id, plat->clk_id);
return 0;
}
#endif
static const struct udevice_id mxs_spi_ids[] = {
{ .compatible = "fsl,imx23-spi" },
{ .compatible = "fsl,imx28-spi" },
{ }
};
U_BOOT_DRIVER(mxs_spi) = {
.name = "mxs_spi",
.id = UCLASS_SPI,
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.of_match = mxs_spi_ids,
.ofdata_to_platdata = mxs_ofdata_to_platdata,
#endif
.priv_auto_alloc_size = sizeof(struct mxs_spi_platdata),
.ops = &mxs_spi_ops,
.priv_auto_alloc_size = sizeof(struct mxs_spi_priv),
.probe = mxs_spi_probe,
};
#endif

View File

@@ -17,6 +17,15 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
config IMX_SCU_THERMAL
bool "Temperature sensor driver for NXP i.MX8"
depends on ARCH_IMX8
help
Support for Temperature sensors on NXP i.MX8.
It supports one critical trip point and one passive trip point. The
boot is hold to the cool device to throttle CPUs when the passive
trip is crossed
config TI_DRA7_THERMAL
bool "Temperature sensor driver for TI dra7xx SOCs"
help

View File

@@ -5,4 +5,5 @@
obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o

View File

@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2019 NXP
*/
#include <config.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <thermal.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <asm/arch/sci/sci.h>
DECLARE_GLOBAL_DATA_PTR;
struct imx_sc_thermal_plat {
int critical;
int alert;
int polling_delay;
int id;
bool zone_node;
};
static int read_temperature(struct udevice *dev, int *temp)
{
s16 celsius;
s8 tenths;
int ret;
sc_rsrc_t *sensor_rsrc = (sc_rsrc_t *)dev_get_driver_data(dev);
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
if (!temp)
return -EINVAL;
ret = sc_misc_get_temp(-1, sensor_rsrc[pdata->id], SC_C_TEMP,
&celsius, &tenths);
if (ret) {
printf("Error: get temperature failed! (error = %d)\n", ret);
return ret;
}
*temp = celsius * 1000 + tenths * 100;
return 0;
}
int imx_sc_thermal_get_temp(struct udevice *dev, int *temp)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
int cpu_temp = 0;
int ret;
ret = read_temperature(dev, &cpu_temp);
if (ret)
return ret;
while (cpu_temp >= pdata->alert) {
printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)",
cpu_temp, pdata->alert, pdata->critical);
puts(" waiting...\n");
mdelay(pdata->polling_delay);
ret = read_temperature(dev, &cpu_temp);
if (ret)
return ret;
}
*temp = cpu_temp / 1000;
return 0;
}
static const struct dm_thermal_ops imx_sc_thermal_ops = {
.get_temp = imx_sc_thermal_get_temp,
};
static int imx_sc_thermal_probe(struct udevice *dev)
{
debug("%s dev name %s\n", __func__, dev->name);
return 0;
}
static int imx_sc_thermal_bind(struct udevice *dev)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
int reg, ret;
int offset;
const char *name;
const void *prop;
debug("%s dev name %s\n", __func__, dev->name);
prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "compatible",
NULL);
if (!prop)
return 0;
pdata->zone_node = 1;
reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "tsens-num", 0);
if (reg == 0) {
printf("%s: no temp sensor number provided!\n", __func__);
return -EINVAL;
}
offset = fdt_subnode_offset(gd->fdt_blob, 0, "thermal-zones");
fdt_for_each_subnode(offset, gd->fdt_blob, offset) {
/* Bind the subnode to this driver */
name = fdt_get_name(gd->fdt_blob, offset, NULL);
ret = device_bind_with_driver_data(dev, dev->driver, name,
dev->driver_data,
offset_to_ofnode(offset),
NULL);
if (ret)
printf("Error binding driver '%s': %d\n",
dev->driver->name, ret);
}
return 0;
}
static int imx_sc_thermal_ofdata_to_platdata(struct udevice *dev)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
struct fdtdec_phandle_args args;
const char *type;
int ret;
int trips_np;
debug("%s dev name %s\n", __func__, dev->name);
if (pdata->zone_node)
return 0;
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
"thermal-sensors",
"#thermal-sensor-cells",
0, 0, &args);
if (ret)
return ret;
if (args.node != dev_of_offset(dev->parent))
return -EFAULT;
if (args.args_count >= 1)
pdata->id = args.args[0];
else
pdata->id = 0;
debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
pdata->polling_delay = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"polling-delay", 1000);
trips_np = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(dev),
"trips");
fdt_for_each_subnode(trips_np, gd->fdt_blob, trips_np) {
type = fdt_getprop(gd->fdt_blob, trips_np, "type", NULL);
if (type) {
if (strcmp(type, "critical") == 0) {
pdata->critical = fdtdec_get_int(gd->fdt_blob,
trips_np,
"temperature",
85);
} else if (strcmp(type, "passive") == 0) {
pdata->alert = fdtdec_get_int(gd->fdt_blob,
trips_np,
"temperature",
80);
}
}
}
debug("id %d polling_delay %d, critical %d, alert %d\n", pdata->id,
pdata->polling_delay, pdata->critical, pdata->alert);
return 0;
}
static const sc_rsrc_t imx8qxp_sensor_rsrc[] = {
SC_R_SYSTEM, SC_R_DRC_0, SC_R_PMIC_0,
SC_R_PMIC_1, SC_R_PMIC_2,
};
static const struct udevice_id imx_sc_thermal_ids[] = {
{ .compatible = "nxp,imx8qxp-sc-tsens", .data =
(ulong)&imx8qxp_sensor_rsrc, },
{ }
};
U_BOOT_DRIVER(imx_sc_thermal) = {
.name = "imx_sc_thermal",
.id = UCLASS_THERMAL,
.ops = &imx_sc_thermal_ops,
.of_match = imx_sc_thermal_ids,
.bind = imx_sc_thermal_bind,
.probe = imx_sc_thermal_probe,
.ofdata_to_platdata = imx_sc_thermal_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct imx_sc_thermal_plat),
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -28,7 +28,7 @@ config BCM2835_WDT
config IMX_WATCHDOG
bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
select HW_WATCHDOG
select HW_WATCHDOG if !WDT
help
Select this to enable the IMX and LSCH2 of Layerscape watchdog
driver.
@@ -169,4 +169,11 @@ config WDT_TANGIER
Intel Tangier SoC. If you're using a board with Intel Tangier
SoC, say Y here.
config SPL_WDT
bool "Enable driver model for watchdog timer drivers in SPL"
depends on SPL_DM
help
Enable driver model for watchdog timer in SPL.
This is similar to CONFIG_WDT in U-Boot.
endmenu

View File

@@ -15,7 +15,7 @@ obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
obj-$(CONFIG_WDT) += wdt-uclass.o
obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o

View File

@@ -5,7 +5,9 @@
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <wdt.h>
#include <watchdog.h>
#include <asm/arch/imx-regs.h>
#ifdef CONFIG_FSL_LSCH2
@@ -13,20 +15,40 @@
#endif
#include <fsl_wdog.h>
#ifdef CONFIG_IMX_WATCHDOG
void hw_watchdog_reset(void)
static void imx_watchdog_expire_now(struct watchdog_regs *wdog)
{
clrsetbits_le16(&wdog->wcr, WCR_WT_MSK, WCR_WDE);
writew(0x5555, &wdog->wsr);
writew(0xaaaa, &wdog->wsr); /* load minimum 1/2 second timeout */
while (1) {
/*
* spin for .5 seconds before reset
*/
}
}
#if !defined(CONFIG_IMX_WATCHDOG) || \
(defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
void __attribute__((weak)) reset_cpu(ulong addr)
{
#ifndef CONFIG_WATCHDOG_RESET_DISABLE
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
imx_watchdog_expire_now(wdog);
}
#endif
#if defined(CONFIG_IMX_WATCHDOG)
static void imx_watchdog_reset(struct watchdog_regs *wdog)
{
#ifndef CONFIG_WATCHDOG_RESET_DISABLE
writew(0x5555, &wdog->wsr);
writew(0xaaaa, &wdog->wsr);
#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
}
void hw_watchdog_init(void)
static void imx_watchdog_init(struct watchdog_regs *wdog)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
u16 timeout;
/*
@@ -44,21 +66,86 @@ void hw_watchdog_init(void)
writew(WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_WDT | WCR_SRS |
WCR_WDA | SET_WCR_WT(timeout), &wdog->wcr);
#endif /* CONFIG_FSL_LSCH2*/
hw_watchdog_reset();
imx_watchdog_reset(wdog);
}
#endif
void __attribute__((weak)) reset_cpu(ulong addr)
#if !CONFIG_IS_ENABLED(WDT)
void hw_watchdog_reset(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
clrsetbits_le16(&wdog->wcr, WCR_WT_MSK, WCR_WDE);
writew(0x5555, &wdog->wsr);
writew(0xaaaa, &wdog->wsr); /* load minimum 1/2 second timeout */
while (1) {
/*
* spin for .5 seconds before reset
*/
}
imx_watchdog_reset(wdog);
}
void hw_watchdog_init(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
imx_watchdog_init(wdog);
}
#else
struct imx_wdt_priv {
void __iomem *base;
};
static int imx_wdt_reset(struct udevice *dev)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_reset(priv->base);
return 0;
}
static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_expire_now(priv->base);
hang();
return 0;
}
static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_init(priv->base);
return 0;
}
static int imx_wdt_probe(struct udevice *dev)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr_ptr(dev);
if (!priv->base)
return -ENOENT;
return 0;
}
static const struct wdt_ops imx_wdt_ops = {
.start = imx_wdt_start,
.reset = imx_wdt_reset,
.expire_now = imx_wdt_expire_now,
};
static const struct udevice_id imx_wdt_ids[] = {
{ .compatible = "fsl,imx21-wdt" },
{}
};
U_BOOT_DRIVER(imx_wdt) = {
.name = "imx_wdt",
.id = UCLASS_WDT,
.of_match = imx_wdt_ids,
.probe = imx_wdt_probe,
.ops = &imx_wdt_ops,
.priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
.flags = DM_FLAG_PRE_RELOC,
};
#endif
#endif