mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-20 09:34:44 +08:00
9bec2b9c61
Currently, unbinding a CCU driver unmaps the device's MMIO region, while leaving its clocks/resets and their providers registered. This can cause a page fault later when some clock operation tries to perform MMIO. Fix this by separating the CCU initialization from the memory allocation, and then using a devres callback to unregister the clocks and resets. This also fixes a memory leak of the `struct ccu_reset`, and uses the correct owner (the specific platform driver) for the clocks and resets. Early OF clock providers are never unregistered, and limited error handling is possible, so they are mostly unchanged. The error reporting is made more consistent by moving the message inside of_sunxi_ccu_probe. Signed-off-by: Samuel Holland <samuel@sholland.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Link: https://lore.kernel.org/r/20210901050526.45673-2-samuel@sholland.org
72 lines
1.6 KiB
C
72 lines
1.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (c) 2016 Maxime Ripard. All rights reserved.
|
|
*/
|
|
|
|
#ifndef _COMMON_H_
|
|
#define _COMMON_H_
|
|
|
|
#include <linux/compiler.h>
|
|
#include <linux/clk-provider.h>
|
|
|
|
#define CCU_FEATURE_FRACTIONAL BIT(0)
|
|
#define CCU_FEATURE_VARIABLE_PREDIV BIT(1)
|
|
#define CCU_FEATURE_FIXED_PREDIV BIT(2)
|
|
#define CCU_FEATURE_FIXED_POSTDIV BIT(3)
|
|
#define CCU_FEATURE_ALL_PREDIV BIT(4)
|
|
#define CCU_FEATURE_LOCK_REG BIT(5)
|
|
#define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
|
|
#define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7)
|
|
|
|
/* MMC timing mode switch bit */
|
|
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
|
|
|
|
struct device_node;
|
|
|
|
struct ccu_common {
|
|
void __iomem *base;
|
|
u16 reg;
|
|
u16 lock_reg;
|
|
u32 prediv;
|
|
|
|
unsigned long features;
|
|
spinlock_t *lock;
|
|
struct clk_hw hw;
|
|
};
|
|
|
|
static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
|
|
{
|
|
return container_of(hw, struct ccu_common, hw);
|
|
}
|
|
|
|
struct sunxi_ccu_desc {
|
|
struct ccu_common **ccu_clks;
|
|
unsigned long num_ccu_clks;
|
|
|
|
struct clk_hw_onecell_data *hw_clks;
|
|
|
|
struct ccu_reset_map *resets;
|
|
unsigned long num_resets;
|
|
};
|
|
|
|
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock);
|
|
|
|
struct ccu_pll_nb {
|
|
struct notifier_block clk_nb;
|
|
struct ccu_common *common;
|
|
|
|
u32 enable;
|
|
u32 lock;
|
|
};
|
|
|
|
#define to_ccu_pll_nb(_nb) container_of(_nb, struct ccu_pll_nb, clk_nb)
|
|
|
|
int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb);
|
|
|
|
int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
|
|
const struct sunxi_ccu_desc *desc);
|
|
void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
|
|
const struct sunxi_ccu_desc *desc);
|
|
|
|
#endif /* _COMMON_H_ */
|