162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015 MediaTek Inc.
462306a36Sopenharmony_ci * Author: James Liao <jamesjj.liao@mediatek.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/of_address.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "clk-mtk.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define REF2USB_TX_EN		BIT(0)
1562306a36Sopenharmony_ci#define REF2USB_TX_LPF_EN	BIT(1)
1662306a36Sopenharmony_ci#define REF2USB_TX_OUT_EN	BIT(2)
1762306a36Sopenharmony_ci#define REF2USB_EN_MASK		(REF2USB_TX_EN | REF2USB_TX_LPF_EN | \
1862306a36Sopenharmony_ci				 REF2USB_TX_OUT_EN)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct mtk_ref2usb_tx {
2162306a36Sopenharmony_ci	struct clk_hw	hw;
2262306a36Sopenharmony_ci	void __iomem	*base_addr;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic inline struct mtk_ref2usb_tx *to_mtk_ref2usb_tx(struct clk_hw *hw)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return container_of(hw, struct mtk_ref2usb_tx, hw);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int mtk_ref2usb_tx_is_prepared(struct clk_hw *hw)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return (readl(tx->base_addr) & REF2USB_EN_MASK) == REF2USB_EN_MASK;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int mtk_ref2usb_tx_prepare(struct clk_hw *hw)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw);
4062306a36Sopenharmony_ci	u32 val;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	val = readl(tx->base_addr);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	val |= REF2USB_TX_EN;
4562306a36Sopenharmony_ci	writel(val, tx->base_addr);
4662306a36Sopenharmony_ci	udelay(100);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	val |= REF2USB_TX_LPF_EN;
4962306a36Sopenharmony_ci	writel(val, tx->base_addr);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	val |= REF2USB_TX_OUT_EN;
5262306a36Sopenharmony_ci	writel(val, tx->base_addr);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void mtk_ref2usb_tx_unprepare(struct clk_hw *hw)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw);
6062306a36Sopenharmony_ci	u32 val;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	val = readl(tx->base_addr);
6362306a36Sopenharmony_ci	val &= ~REF2USB_EN_MASK;
6462306a36Sopenharmony_ci	writel(val, tx->base_addr);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct clk_ops mtk_ref2usb_tx_ops = {
6862306a36Sopenharmony_ci	.is_prepared	= mtk_ref2usb_tx_is_prepared,
6962306a36Sopenharmony_ci	.prepare	= mtk_ref2usb_tx_prepare,
7062306a36Sopenharmony_ci	.unprepare	= mtk_ref2usb_tx_unprepare,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct clk_hw *mtk_clk_register_ref2usb_tx(const char *name,
7462306a36Sopenharmony_ci			const char *parent_name, void __iomem *reg)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct mtk_ref2usb_tx *tx;
7762306a36Sopenharmony_ci	struct clk_init_data init = {};
7862306a36Sopenharmony_ci	int ret;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	tx = kzalloc(sizeof(*tx), GFP_KERNEL);
8162306a36Sopenharmony_ci	if (!tx)
8262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	tx->base_addr = reg;
8562306a36Sopenharmony_ci	tx->hw.init = &init;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	init.name = name;
8862306a36Sopenharmony_ci	init.ops = &mtk_ref2usb_tx_ops;
8962306a36Sopenharmony_ci	init.parent_names = &parent_name;
9062306a36Sopenharmony_ci	init.num_parents = 1;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ret = clk_hw_register(NULL, &tx->hw);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (ret) {
9562306a36Sopenharmony_ci		kfree(tx);
9662306a36Sopenharmony_ci		return ERR_PTR(ret);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return &tx->hw;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_register_ref2usb_tx);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid mtk_clk_unregister_ref2usb_tx(struct clk_hw *hw)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	clk_hw_unregister(hw);
10862306a36Sopenharmony_ci	kfree(tx);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_unregister_ref2usb_tx);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
113