| /* |
| * Copyright (c) 2015 MediaTek Inc. |
| * Author: James Liao <jamesjj.liao@mediatek.com> |
| * |
| * 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. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/of_address.h> |
| #include <linux/slab.h> |
| |
| #include "clk-mtk.h" |
| |
| #define REF2USB_TX_EN BIT(0) |
| #define REF2USB_TX_LPF_EN BIT(1) |
| #define REF2USB_TX_OUT_EN BIT(2) |
| #define REF2USB_EN_MASK (REF2USB_TX_EN | REF2USB_TX_LPF_EN | \ |
| REF2USB_TX_OUT_EN) |
| |
| struct mtk_ref2usb_tx { |
| struct clk_hw hw; |
| void __iomem *base_addr; |
| }; |
| |
| static inline struct mtk_ref2usb_tx *to_mtk_ref2usb_tx(struct clk_hw *hw) |
| { |
| return container_of(hw, struct mtk_ref2usb_tx, hw); |
| } |
| |
| static int mtk_ref2usb_tx_is_prepared(struct clk_hw *hw) |
| { |
| struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); |
| |
| return (readl(tx->base_addr) & REF2USB_EN_MASK) == REF2USB_EN_MASK; |
| } |
| |
| static int mtk_ref2usb_tx_prepare(struct clk_hw *hw) |
| { |
| struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); |
| u32 val; |
| |
| val = readl(tx->base_addr); |
| |
| val |= REF2USB_TX_EN; |
| writel(val, tx->base_addr); |
| udelay(100); |
| |
| val |= REF2USB_TX_LPF_EN; |
| writel(val, tx->base_addr); |
| |
| val |= REF2USB_TX_OUT_EN; |
| writel(val, tx->base_addr); |
| |
| return 0; |
| } |
| |
| static void mtk_ref2usb_tx_unprepare(struct clk_hw *hw) |
| { |
| struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); |
| u32 val; |
| |
| val = readl(tx->base_addr); |
| val &= ~REF2USB_EN_MASK; |
| writel(val, tx->base_addr); |
| } |
| |
| static const struct clk_ops mtk_ref2usb_tx_ops = { |
| .is_prepared = mtk_ref2usb_tx_is_prepared, |
| .prepare = mtk_ref2usb_tx_prepare, |
| .unprepare = mtk_ref2usb_tx_unprepare, |
| }; |
| |
| struct clk * __init mtk_clk_register_ref2usb_tx(const char *name, |
| const char *parent_name, void __iomem *reg) |
| { |
| struct mtk_ref2usb_tx *tx; |
| struct clk_init_data init = {}; |
| struct clk *clk; |
| |
| tx = kzalloc(sizeof(*tx), GFP_KERNEL); |
| if (!tx) |
| return ERR_PTR(-ENOMEM); |
| |
| tx->base_addr = reg; |
| tx->hw.init = &init; |
| |
| init.name = name; |
| init.ops = &mtk_ref2usb_tx_ops; |
| init.parent_names = &parent_name; |
| init.num_parents = 1; |
| |
| clk = clk_register(NULL, &tx->hw); |
| |
| if (IS_ERR(clk)) { |
| pr_err("Failed to register clk %s: %ld\n", name, PTR_ERR(clk)); |
| kfree(tx); |
| } |
| |
| return clk; |
| } |