11541Srgrimes// SPDX-License-Identifier: GPL-2.0-or-later
21541Srgrimes/*
31541Srgrimes * MMP PMU power island support
41541Srgrimes *
51541Srgrimes * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
61541Srgrimes */
71541Srgrimes
81541Srgrimes#include <linux/pm_domain.h>
91541Srgrimes#include <linux/slab.h>
101541Srgrimes#include <linux/io.h>
111541Srgrimes
121541Srgrimes#include "clk.h"
131541Srgrimes
141541Srgrimes#define to_mmp_pm_domain(genpd) container_of(genpd, struct mmp_pm_domain, genpd)
151541Srgrimes
161541Srgrimesstruct mmp_pm_domain {
171541Srgrimes	struct generic_pm_domain genpd;
181541Srgrimes	void __iomem *reg;
191541Srgrimes	spinlock_t *lock;
201541Srgrimes	u32 power_on;
211541Srgrimes	u32 reset;
221541Srgrimes	u32 clock_enable;
231541Srgrimes	unsigned int flags;
241541Srgrimes};
251541Srgrimes
261541Srgrimesstatic int mmp_pm_domain_power_on(struct generic_pm_domain *genpd)
271541Srgrimes{
281541Srgrimes	struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
291541Srgrimes	unsigned long flags = 0;
301541Srgrimes	u32 val;
311541Srgrimes
321541Srgrimes	if (pm_domain->lock)
331541Srgrimes		spin_lock_irqsave(pm_domain->lock, flags);
3450477Speter
351541Srgrimes	val = readl(pm_domain->reg);
361541Srgrimes
372165Spaul	/* Turn on the power island */
382811Sbde	val |= pm_domain->power_on;
392165Spaul	writel(val, pm_domain->reg);
4076166Smarkm
4176166Smarkm	/* Disable isolation */
4276166Smarkm	val |= 0x100;
4376166Smarkm	writel(val, pm_domain->reg);
4476166Smarkm
4576166Smarkm	/* Some blocks need to be reset after a power up */
461541Srgrimes	if (pm_domain->reset || pm_domain->clock_enable) {
47101848Srwatson		u32 after_power_on = val;
4876166Smarkm
49103187Sbde		val &= ~pm_domain->reset;
5076166Smarkm		writel(val, pm_domain->reg);
51103187Sbde
5270834Swollman		val |= pm_domain->clock_enable;
5334924Sbde		writel(val, pm_domain->reg);
5454803Srwatson
55103849Sjeff		val |= pm_domain->reset;
561541Srgrimes		writel(val, pm_domain->reg);
571541Srgrimes
581541Srgrimes		writel(after_power_on, pm_domain->reg);
591541Srgrimes	}
601541Srgrimes
611541Srgrimes	if (pm_domain->lock)
621541Srgrimes		spin_unlock_irqrestore(pm_domain->lock, flags);
631541Srgrimes
641541Srgrimes	return 0;
651541Srgrimes}
661541Srgrimes
671541Srgrimesstatic int mmp_pm_domain_power_off(struct generic_pm_domain *genpd)
681541Srgrimes{
691541Srgrimes	struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
701541Srgrimes	unsigned long flags = 0;
711541Srgrimes	u32 val;
7260938Sjake
731541Srgrimes	if (pm_domain->flags & MMP_PM_DOMAIN_NO_DISABLE)
7492719Salfred		return 0;
7559652Sgreen
7612158Sbde	if (pm_domain->lock)
7790791Sphk		spin_lock_irqsave(pm_domain->lock, flags);
7890791Sphk
7990791Sphk	/* Turn off and isolate the power island. */
8090791Sphk	val = readl(pm_domain->reg);
8190791Sphk	val &= ~pm_domain->power_on;
8290791Sphk	val &= ~0x100;
8390791Sphk	writel(val, pm_domain->reg);
8422521Sdyson
8522521Sdyson	if (pm_domain->lock)
86101368Sjeff		spin_unlock_irqrestore(pm_domain->lock, flags);
87101368Sjeff
88103926Sjeff	return 0;
89101491Sjeff}
90101491Sjeff
91101491Sjeffstruct generic_pm_domain *mmp_pm_domain_register(const char *name,
92101491Sjeff		void __iomem *reg,
93103926Sjeff		u32 power_on, u32 reset, u32 clock_enable,
94103926Sjeff		unsigned int flags, spinlock_t *lock)
95103926Sjeff{
96101491Sjeff	struct mmp_pm_domain *pm_domain;
97101491Sjeff
98101491Sjeff	pm_domain = kzalloc(sizeof(*pm_domain), GFP_KERNEL);
99101491Sjeff	if (!pm_domain)
10022521Sdyson		return ERR_PTR(-ENOMEM);
101101368Sjeff
1021541Srgrimes	pm_domain->reg = reg;
103101491Sjeff	pm_domain->power_on = power_on;
104101491Sjeff	pm_domain->reset = reset;
105101491Sjeff	pm_domain->clock_enable = clock_enable;
106101491Sjeff	pm_domain->flags = flags;
107101491Sjeff	pm_domain->lock = lock;
108101491Sjeff
109103926Sjeff	pm_genpd_init(&pm_domain->genpd, NULL, true);
110103926Sjeff	pm_domain->genpd.name = name;
111103926Sjeff	pm_domain->genpd.power_on = mmp_pm_domain_power_on;
112103926Sjeff	pm_domain->genpd.power_off = mmp_pm_domain_power_off;
113101491Sjeff
114103926Sjeff	return &pm_domain->genpd;
115103926Sjeff}
116103926Sjeff