// SPDX-License-Identifier: GPL-2.0
/*
* Renesas Clock Pulse Generator / Module Standby and Software Reset
*
* Copyright (C) 2015 Glider bvba
*
* Based on clk-mstp.c, clk-rcar-gen2.c, and clk-rcar-gen3.c
*
* Copyright (C) 2013 Ideas On Board SPRL
* Copyright (C) 2015 Renesas Electronics Corp.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/psci.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <dt-bindings/clock/renesas-cpg-mssr.h>
#include "renesas-cpg-mssr.h"
#include "clk-div6.h"
#ifdef DEBUG
#define WARN_DEBUG(x) WARN_ON(x)
#else
#define WARN_DEBUG(x) do { } while (0)
#endif
/*
* Module Standby and Software Reset register offets.
*
* If the registers exist, these are valid for SH-Mobile, R-Mobile,
* R-Car Gen2, R-Car Gen3, and RZ/G1.
* These are NOT valid for R-Car Gen1 and RZ/A1!
*/
/*
* Module Stop Status Register offsets
*/
static const u16 mstpsr[] = {
0x030, 0x038, 0x040, 0x048, 0x04C, 0x03C, 0x1C0, 0x1C4,
0x9A0, 0x9A4, 0x9A8, 0x9AC,
};
#define MSTPSR(i) mstpsr[i]
/*
* System Module Stop Control Register offsets
*/
static const u16 smstpcr[] = {
0x130, 0x134, 0x138, 0x13C, 0x140, 0x144, 0x148, 0x14C,
0x990, 0x994, 0x998, 0x99C,
};
#define SMSTPCR(i) smstpcr[i]
/*
* Standby Control Register offsets (RZ/A)
* Base address is FRQCR register
*/
static const u16 stbcr[] = {
0xFFFF/*dummy*/, 0x010, 0x014, 0x410, 0x414, 0x418, 0x41C, 0x420,
0x424, 0x428, 0x42C,
};
#define STBCR(i) stbcr[i]
/*
* Software Reset Register offsets
*/
static const u16 srcr[] = {
0x0A0, 0x0A8, 0x0B0, 0x0B8, 0x0BC, 0x0C4, 0x1C8, 0x1CC,
0x920, 0x924, 0x928, 0x92C,
};
#define SRCR(i) srcr[i]
/* Realtime Module Stop Control Register offsets */
#define RMSTPCR(i) (smstpcr[i] - 0x20)
/* Modem Module Stop Control Register offsets (r8a73a4) */
#define MMSTPCR(i) (smstpcr[i] + 0x20)
/* Software Reset Clearing Register offsets */
#define SRSTCLR(i) (0x940 + (i) * 4)
/**
* Clock Pulse Generator / Module Standby and Software Reset Private Data
*
* @rcdev: Optional reset controller entity
* @dev: CPG/MSSR device
* @base: CPG/MSSR register block base address
* @rmw_lock: protects RMW register accesses
* @np: Device node in DT for this CPG/MSSR module
* @num_core_clks: Number of Core Clocks in clks[]
* @num_mod_clks: Number of Module Clocks in clks[]
* @last_dt_core_clk: ID of the last Core Clock exported to DT
* @stbyctrl: This device has Standby Control Registers
* @notifiers: Notifier chain to save/restore clock state for system resume
* @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control
* @smstpcr_saved[].val: Saved values of SMSTPCR[]
* @clks: Array containing all Core and Module Clocks
*/
struct cpg_mssr_priv {
#ifdef CONFIG_RESET_CONTROLLER
struct reset_controller_dev rcdev;
#endif
struct device *dev;
void __iomem *base;
spinlock_t rmw_lock;
struct device_node *np;
unsigned int num_core_clks;
unsigned int num_mod_clks;
unsigned int last_dt_core_clk;
bool stbyctrl;
struct raw_notifier_head notifiers;
struct {
u32 mask;
u32 val;
} smstpcr_saved[ARRAY_SIZE(smstpcr)];
struct clk *clks[];
};
static struct cpg_mssr_priv *cpg_mssr_priv;
/**
* struct mstp_clock - MSTP gating clock
* @hw: handle between common and hardware-specific interfaces
* @index: MSTP clock number
* @priv: CPG/MSSR private data
*/
struct mstp_clock {
struct clk_hw hw;
u32 index;
struct cpg_mssr_priv *priv;
};
#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
{
struct mstp_clock *clock = to_mstp_clock(hw);
struct cpg_mssr_priv *priv = clock->priv;
unsigned int reg = clock->index / 32;
unsigned int bit = clock->index % 32;
struct device *dev = priv->dev;
u32 bitmask = BIT(bit);
unsigned long flags;
unsigned int i;
u32 value;
dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk,
enable ? "ON" : "OFF");
spin_lock_irqsave(&priv->rmw_lock, flags);
if (priv->stbyctrl) {
value = readb(priv->base