Merge tag 'u-boot-imx-20190719' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
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:
@@ -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
|
||||
|
||||
@@ -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
155
drivers/clk/clk-divider.c
Normal 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,
|
||||
};
|
||||
80
drivers/clk/clk-fixed-factor.c
Normal file
80
drivers/clk/clk-fixed-factor.c
Normal 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
172
drivers/clk/clk-mux.c
Normal 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,
|
||||
};
|
||||
@@ -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
57
drivers/clk/clk.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
185
drivers/clk/clk_sandbox_ccf.c
Normal file
185
drivers/clk/clk_sandbox_ccf.c
Normal 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", ®, 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", ®, 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", ®, 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
103
drivers/clk/imx/clk-gate2.c
Normal 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
179
drivers/clk/imx/clk-imx6q.c
Normal 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
90
drivers/clk/imx/clk-pfd.c
Normal 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,
|
||||
};
|
||||
82
drivers/clk/imx/clk-pllv3.c
Normal file
82
drivers/clk/imx/clk-pllv3.c
Normal 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
69
drivers/clk/imx/clk.h
Normal 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 */
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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) >> 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_set);
|
||||
else
|
||||
writel(BIT(offset), ®->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_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_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) >> 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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
190
drivers/pinctrl/nxp/pinctrl-mxs.c
Normal file
190
drivers/pinctrl/nxp/pinctrl-mxs.c
Normal 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,
|
||||
};
|
||||
61
drivers/pinctrl/nxp/pinctrl-mxs.h
Normal file
61
drivers/pinctrl/nxp/pinctrl-mxs.h
Normal 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 */
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
468
drivers/power/regulator/bd71837.c
Normal file
468
drivers/power/regulator/bd71837.c
Normal 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),
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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(®s->cr, PWMCR_EN);
|
||||
else
|
||||
clrbits_le32(®s->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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
203
drivers/thermal/imx_scu_thermal.c
Normal file
203
drivers/thermal/imx_scu_thermal.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user