2015-02-09 10:47:33

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH v5]: clk: Add common clock support for Mediatek MT8135 and MT8173

This patchset contains the initial common clock support for Mediatek SoCs.
Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates.

This patchset also contains a basic clock support for Mediatek MT8135 and MT8173.

Also included is now my PMIC wrapper series as of v3. Since it now depends on
the clock drivers I can no longer post it independenty.

This driver is based on 3.19-rc1 + MT8135 and MT8173 basic support.

Changes in v2:
- Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch.

Changes in v3:
- Rebase to 3.19-rc1.
- Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs.

Changes in v4:
- Support MT8173 platform.
- Re-ordered patchset. driver/clk/Makefile in 2nd patch.
- Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c.
- Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions.
- Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor.
- Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock.
- Example above include a node for the clock controller itself, followed by the i2c controller example above.

Changes in v5:
- Add reset controller support for pericfg/infracfg
- Use regmap for the gates
- remove now unnecessary spinlock for the gates
- Add PMIC wrapper support as of v3

Sascha


2015-02-09 10:47:51

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 01/13] clk: dts: mediatek: add Mediatek MT8135 clock bindings

From: James Liao <[email protected]>

Document the device-tree binding of Mediatek MT8135 SoC, including
TOPCKGEN, PLLs, INFRA and PERI clock controller.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
.../bindings/clock/mediatek,mt8135-clock.txt | 44 +++++
include/dt-bindings/clock/mt8135-clk.h | 190 +++++++++++++++++++++
2 files changed, 234 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
create mode 100644 include/dt-bindings/clock/mt8135-clk.h

diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
new file mode 100644
index 0000000..1e3566f
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
@@ -0,0 +1,44 @@
+Mediatek MT8135 Clock Controller
+
+This binding uses the common clock binding:
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The Mediatek MT8135 clock controller generates and supplies clock to various
+controllers within Mediatek MT8135 SoC.
+
+Required Properties:
+
+- compatible: should be one of following:
+ - "mediatek,mt8135-topckgen" : for topckgen clock controller of MT8135.
+ - "mediatek,mt8135-apmixedsys" : for apmixed_sys (PLLs) of MT8135.
+ - "mediatek,mt8135-infracfg" : for infra_sys clock controller of MT8135.
+ - "mediatek,mt8135-pericfg" : for peri_sys clock controller of MT8135.
+
+- reg: physical base address of the controller and length of memory mapped
+ region.
+
+- #clock-cells: should be 1.
+
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/mt8135-clk.h header and can be used in device tree sources.
+
+Example: I2C controller node that consumes the clock generated by the clock
+ controller (refer to the standard clock bindings for information about
+ "clocks" and "clock-names" properties):
+
+ pericfg: pericfg@10003000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "mediatek,mt8135-pericfg";
+ reg = <0 0x10003000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ i2c0: i2c@1100d000 {
+ compatible = "mediatek,mt8135-i2c", "mediatek,mt6589-i2c";
+ reg = <0 0x1100d000 0 0x70>, <0 0x11000300 0 0x80>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_LOW>;
+ clock-div = <16>;
+ clocks = <&pericfg PERI_I2C0_CK>, <&pericfg PERI_AP_DMA_CK>;
+ clock-names = "main", "dma";
+ };
diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h
new file mode 100644
index 0000000..8aea762
--- /dev/null
+++ b/include/dt-bindings/clock/mt8135-clk.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_CLK_MT8135_H
+#define _DT_BINDINGS_CLK_MT8135_H
+
+/* TOPCKGEN */
+
+#define TOP_DSI0_LNTC_DSICLK 1
+#define TOP_HDMITX_CLKDIG_CTS 2
+#define TOP_CLKPH_MCK 3
+#define TOP_CPUM_TCK_IN 4
+#define TOP_MAINPLL_806M 5
+#define TOP_MAINPLL_537P3M 6
+#define TOP_MAINPLL_322P4M 7
+#define TOP_MAINPLL_230P3M 8
+#define TOP_UNIVPLL_624M 9
+#define TOP_UNIVPLL_416M 10
+#define TOP_UNIVPLL_249P6M 11
+#define TOP_UNIVPLL_178P3M 12
+#define TOP_UNIVPLL_48M 13
+#define TOP_MMPLL_D2 14
+#define TOP_MMPLL_D3 15
+#define TOP_MMPLL_D5 16
+#define TOP_MMPLL_D7 17
+#define TOP_MMPLL_D4 18
+#define TOP_MMPLL_D6 19
+#define TOP_SYSPLL_D2 20
+#define TOP_SYSPLL_D4 21
+#define TOP_SYSPLL_D6 22
+#define TOP_SYSPLL_D8 23
+#define TOP_SYSPLL_D10 24
+#define TOP_SYSPLL_D12 25
+#define TOP_SYSPLL_D16 26
+#define TOP_SYSPLL_D24 27
+#define TOP_SYSPLL_D3 28
+#define TOP_SYSPLL_D2P5 29
+#define TOP_SYSPLL_D5 30
+#define TOP_SYSPLL_D3P5 31
+#define TOP_UNIVPLL1_D2 32
+#define TOP_UNIVPLL1_D4 33
+#define TOP_UNIVPLL1_D6 34
+#define TOP_UNIVPLL1_D8 35
+#define TOP_UNIVPLL1_D10 36
+#define TOP_UNIVPLL2_D2 37
+#define TOP_UNIVPLL2_D4 38
+#define TOP_UNIVPLL2_D6 39
+#define TOP_UNIVPLL2_D8 40
+#define TOP_UNIVPLL_D3 41
+#define TOP_UNIVPLL_D5 42
+#define TOP_UNIVPLL_D7 43
+#define TOP_UNIVPLL_D10 44
+#define TOP_UNIVPLL_D26 45
+#define TOP_APLL_CK 46
+#define TOP_APLL_D4 47
+#define TOP_APLL_D8 48
+#define TOP_APLL_D16 49
+#define TOP_APLL_D24 50
+#define TOP_LVDSPLL_D2 51
+#define TOP_LVDSPLL_D4 52
+#define TOP_LVDSPLL_D8 53
+#define TOP_LVDSTX_CLKDIG_CT 54
+#define TOP_VPLL_DPIX_CK 55
+#define TOP_TVHDMI_H_CK 56
+#define TOP_HDMITX_CLKDIG_D2 57
+#define TOP_HDMITX_CLKDIG_D3 58
+#define TOP_TVHDMI_D2 59
+#define TOP_TVHDMI_D4 60
+#define TOP_MEMPLL_MCK_D4 61
+#define TOP_AXI_SEL 62
+#define TOP_SMI_SEL 63
+#define TOP_MFG_SEL 64
+#define TOP_IRDA_SEL 65
+#define TOP_CAM_SEL 66
+#define TOP_AUD_INTBUS_SEL 67
+#define TOP_JPG_SEL 68
+#define TOP_DISP_SEL 69
+#define TOP_MSDC30_1_SEL 70
+#define TOP_MSDC30_2_SEL 71
+#define TOP_MSDC30_3_SEL 72
+#define TOP_MSDC30_4_SEL 73
+#define TOP_USB20_SEL 74
+#define TOP_VENC_SEL 75
+#define TOP_SPI_SEL 76
+#define TOP_UART_SEL 77
+#define TOP_MEM_SEL 78
+#define TOP_CAMTG_SEL 79
+#define TOP_AUDIO_SEL 80
+#define TOP_FIX_SEL 81
+#define TOP_VDEC_SEL 82
+#define TOP_DDRPHYCFG_SEL 83
+#define TOP_DPILVDS_SEL 84
+#define TOP_PMICSPI_SEL 85
+#define TOP_MSDC30_0_SEL 86
+#define TOP_SMI_MFG_AS_SEL 87
+#define TOP_GCPU_SEL 88
+#define TOP_DPI1_SEL 89
+#define TOP_CCI_SEL 90
+#define TOP_APLL_SEL 91
+#define TOP_HDMIPLL_SEL 92
+#define TOP_NR_CLK 93
+
+/* APMIXED_SYS */
+
+#define APMIXED_ARMPLL1 1
+#define APMIXED_ARMPLL2 2
+#define APMIXED_MAINPLL 3
+#define APMIXED_UNIVPLL 4
+#define APMIXED_MMPLL 5
+#define APMIXED_MSDCPLL 6
+#define APMIXED_TVDPLL 7
+#define APMIXED_LVDSPLL 8
+#define APMIXED_AUDPLL 9
+#define APMIXED_VDECPLL 10
+#define APMIXED_NR_CLK 11
+
+/* INFRA_SYS */
+
+#define INFRA_PMIC_WRAP_CK 1
+#define INFRA_PMICSPI_CK 2
+#define INFRA_CCIF1_AP_CTRL 3
+#define INFRA_CCIF0_AP_CTRL 4
+#define INFRA_KP_CK 5
+#define INFRA_CPUM_CK 6
+#define INFRA_M4U_CK 7
+#define INFRA_MFGAXI_CK 8
+#define INFRA_DEVAPC_CK 9
+#define INFRA_AUDIO_CK 10
+#define INFRA_MFG_BUS_CK 11
+#define INFRA_SMI_CK 12
+#define INFRA_DBGCLK_CK 13
+#define INFRA_NR_CLK 14
+
+/* PERI_SYS */
+
+#define PERI_I2C5_CK 1
+#define PERI_I2C4_CK 2
+#define PERI_I2C3_CK 3
+#define PERI_I2C2_CK 4
+#define PERI_I2C1_CK 5
+#define PERI_I2C0_CK 6
+#define PERI_UART3_CK 7
+#define PERI_UART2_CK 8
+#define PERI_UART1_CK 9
+#define PERI_UART0_CK 10
+#define PERI_IRDA_CK 11
+#define PERI_NLI_CK 12
+#define PERI_MD_HIF_CK 13
+#define PERI_AP_HIF_CK 14
+#define PERI_MSDC30_3_CK 15
+#define PERI_MSDC30_2_CK 16
+#define PERI_MSDC30_1_CK 17
+#define PERI_MSDC20_2_CK 18
+#define PERI_MSDC20_1_CK 19
+#define PERI_AP_DMA_CK 20
+#define PERI_USB1_CK 21
+#define PERI_USB0_CK 22
+#define PERI_PWM_CK 23
+#define PERI_PWM7_CK 24
+#define PERI_PWM6_CK 25
+#define PERI_PWM5_CK 26
+#define PERI_PWM4_CK 27
+#define PERI_PWM3_CK 28
+#define PERI_PWM2_CK 29
+#define PERI_PWM1_CK 30
+#define PERI_THERM_CK 31
+#define PERI_NFI_CK 32
+#define PERI_USBSLV_CK 33
+#define PERI_USB1_MCU_CK 34
+#define PERI_USB0_MCU_CK 35
+#define PERI_GCPU_CK 36
+#define PERI_FHCTL_CK 37
+#define PERI_SPI1_CK 38
+#define PERI_AUXADC_CK 39
+#define PERI_PERI_PWRAP_CK 40
+#define PERI_I2C6_CK 41
+#define PERI_NR_CLK 42
+
+#endif /* _DT_BINDINGS_CLK_MT8135_H */
--
2.1.4

2015-02-09 10:51:09

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 02/13] clk: mediatek: Add initial common clock support for Mediatek SoCs.

From: James Liao <[email protected]>

This patch adds common clock support for Mediatek SoCs, including plls,
muxes and clock gates.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/clk/Makefile | 1 +
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-gate.h | 49 +++++++++++++
drivers/clk/mediatek/clk-mtk.c | 155 ++++++++++++++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-mtk.h | 133 ++++++++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-pll.c | 63 ++++++++++++++++
drivers/clk/mediatek/clk-pll.h | 52 ++++++++++++++
8 files changed, 591 insertions(+)
create mode 100644 drivers/clk/mediatek/Makefile
create mode 100644 drivers/clk/mediatek/clk-gate.c
create mode 100644 drivers/clk/mediatek/clk-gate.h
create mode 100644 drivers/clk/mediatek/clk-mtk.c
create mode 100644 drivers/clk/mediatek/clk-mtk.h
create mode 100644 drivers/clk/mediatek/clk-pll.c
create mode 100644 drivers/clk/mediatek/clk-pll.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..ce6c250 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
obj-$(CONFIG_ARCH_HIP04) += hisilicon/
obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
+obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
new file mode 100644
index 0000000..c384e97
--- /dev/null
+++ b/drivers/clk/mediatek/Makefile
@@ -0,0 +1 @@
+obj-y += clk-mtk.o clk-pll.o clk-gate.o
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
new file mode 100644
index 0000000..9d77ee3
--- /dev/null
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
+{
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+ u32 val;
+
+ regmap_read(cg->regmap, cg->sta_ofs, &val);
+
+ val &= BIT(cg->bit);
+
+ return val == 0;
+}
+
+static int mtk_cg_bit_is_set(struct clk_hw *hw)
+{
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+ u32 val;
+
+ regmap_read(cg->regmap, cg->sta_ofs, &val);
+
+ val &= BIT(cg->bit);
+
+ return val != 0;
+}
+
+static void mtk_cg_set_bit(struct clk_hw *hw)
+{
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+
+ regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
+}
+
+static void mtk_cg_clr_bit(struct clk_hw *hw)
+{
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+
+ regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
+}
+
+static int mtk_cg_enable(struct clk_hw *hw)
+{
+ mtk_cg_clr_bit(hw);
+
+ return 0;
+}
+
+static void mtk_cg_disable(struct clk_hw *hw)
+{
+ mtk_cg_set_bit(hw);
+}
+
+static int mtk_cg_enable_inv(struct clk_hw *hw)
+{
+ mtk_cg_set_bit(hw);
+
+ return 0;
+}
+
+static void mtk_cg_disable_inv(struct clk_hw *hw)
+{
+ mtk_cg_clr_bit(hw);
+}
+
+const struct clk_ops mtk_clk_gate_ops_setclr = {
+ .is_enabled = mtk_cg_bit_is_cleared,
+ .enable = mtk_cg_enable,
+ .disable = mtk_cg_disable,
+};
+
+const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
+ .is_enabled = mtk_cg_bit_is_set,
+ .enable = mtk_cg_enable_inv,
+ .disable = mtk_cg_disable_inv,
+};
+
+struct clk *mtk_clk_register_gate(
+ const char *name,
+ const char *parent_name,
+ struct regmap *regmap,
+ int set_ofs,
+ int clr_ofs,
+ int sta_ofs,
+ u8 bit,
+ const struct clk_ops *ops)
+{
+ struct mtk_clk_gate *cg;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ cg = kzalloc(sizeof(*cg), GFP_KERNEL);
+ if (!cg)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.ops = ops;
+
+ cg->regmap = regmap;
+ cg->set_ofs = set_ofs;
+ cg->clr_ofs = clr_ofs;
+ cg->sta_ofs = sta_ofs;
+ cg->bit = bit;
+
+ cg->hw.init = &init;
+
+ clk = clk_register(NULL, &cg->hw);
+ if (IS_ERR(clk))
+ kfree(cg);
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
new file mode 100644
index 0000000..a44dcbf
--- /dev/null
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __DRV_CLK_GATE_H
+#define __DRV_CLK_GATE_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+struct mtk_clk_gate {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ int set_ofs;
+ int clr_ofs;
+ int sta_ofs;
+ u8 bit;
+};
+
+#define to_clk_gate(_hw) container_of(_hw, struct mtk_clk_gate, hw)
+
+extern const struct clk_ops mtk_clk_gate_ops_setclr;
+extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
+
+struct clk *mtk_clk_register_gate(
+ const char *name,
+ const char *parent_name,
+ struct regmap *regmap,
+ int set_ofs,
+ int clr_ofs,
+ int sta_ofs,
+ u8 bit,
+ const struct clk_ops *ops);
+
+#endif /* __DRV_CLK_GATE_H */
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
new file mode 100644
index 0000000..479857c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+void mtk_init_factors(struct mtk_fixed_factor *clks, int num,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < num; i++) {
+ struct mtk_fixed_factor *ff = &clks[i];
+
+ clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
+ CLK_SET_RATE_PARENT, ff->mult, ff->div);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ ff->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[ff->id] = clk;
+ }
+}
+
+void mtk_init_clk_gates(struct regmap *regmap,
+ struct mtk_gate *clks, int num,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < num; i++) {
+ struct mtk_gate *gate = &clks[i];
+
+ clk = mtk_clk_register_gate(gate->name, gate->parent_name,
+ regmap,
+ gate->regs->set_ofs,
+ gate->regs->clr_ofs,
+ gate->regs->sta_ofs,
+ gate->shift, gate->ops);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[gate->id] = clk;
+ }
+}
+
+struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
+{
+ int i;
+ struct clk_onecell_data *clk_data;
+
+ clk_data = kzalloc(sizeof(clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return NULL;
+
+ clk_data->clks = kcalloc(clk_num, sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return NULL;
+ }
+
+ clk_data->clk_num = clk_num;
+
+ for (i = 0; i < clk_num; ++i)
+ clk_data->clks[i] = ERR_PTR(-ENOENT);
+
+ return clk_data;
+}
+
+struct clk *mtk_clk_register_mux(
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ void __iomem *base_addr,
+ u8 shift,
+ u8 width,
+ u8 gate_bit,
+ spinlock_t *lock)
+{
+ struct clk *clk;
+ struct clk_mux *mux;
+ struct clk_gate *gate = NULL;
+ struct clk_hw *gate_hw = NULL;
+ const struct clk_ops *gate_ops = NULL;
+ u32 mask = BIT(width) - 1;
+
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = base_addr;
+ mux->mask = mask;
+ mux->shift = shift;
+ mux->flags = 0;
+ mux->lock = lock;
+
+ if (gate_bit <= MAX_MUX_GATE_BIT) {
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ gate->reg = base_addr;
+ gate->bit_idx = gate_bit;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = lock;
+
+ gate_hw = &gate->hw;
+ gate_ops = &clk_gate_ops;
+ }
+
+ clk = clk_register_composite(NULL, name, parent_names, num_parents,
+ &mux->hw, &clk_mux_ops,
+ NULL, NULL,
+ gate_hw, gate_ops,
+ CLK_SET_RATE_PARENT);
+
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(mux);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
new file mode 100644
index 0000000..35cf9a3
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __DRV_CLK_MTK_H
+#define __DRV_CLK_MTK_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#define MAX_MUX_GATE_BIT 31
+#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1)
+
+struct mtk_fixed_factor {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int mult;
+ int div;
+};
+
+#define FACTOR(_id, _name, _parent, _mult, _div) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .mult = _mult, \
+ .div = _div, \
+ }
+
+extern void mtk_init_factors(struct mtk_fixed_factor *clks, int num,
+ struct clk_onecell_data *clk_data);
+
+struct mtk_mux {
+ int id;
+ const char *name;
+ uint32_t reg;
+ int shift;
+ int width;
+ int gate;
+ const char **parent_names;
+ int num_parents;
+};
+
+#define MUX(_id, _name, _parents, _reg, _shift, _width, _gate) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .gate = _gate, \
+ .parent_names = (const char **)_parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ }
+
+struct mtk_pll {
+ int id;
+ const char *name;
+ const char *parent_name;
+ uint32_t reg;
+ uint32_t pwr_reg;
+ uint32_t en_mask;
+ unsigned int flags;
+ const struct clk_ops *ops;
+};
+
+#define PLL(_id, _name, _parent, _reg, _pwr_reg, _en_mask, _flags, _ops) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .ops = _ops, \
+ }
+
+struct mtk_gate_regs {
+ u32 sta_ofs;
+ u32 clr_ofs;
+ u32 set_ofs;
+};
+
+struct mtk_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ struct mtk_gate_regs *regs;
+ int shift;
+ const struct clk_ops *ops;
+};
+
+#define GATE(_id, _name, _parent, _regs, _shift, _ops) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &_regs, \
+ .shift = _shift, \
+ .ops = _ops, \
+ }
+
+void mtk_init_clk_gates(struct regmap *regmap,
+ struct mtk_gate *clks, int num,
+ struct clk_onecell_data *clk_data);
+
+struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
+
+struct clk *mtk_clk_register_mux(
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ void __iomem *base_addr,
+ u8 shift,
+ u8 width,
+ u8 gate_bit,
+ spinlock_t *lock);
+
+#endif /* __DRV_CLK_MTK_H */
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
new file mode 100644
index 0000000..59dee83
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/io.h>
+#include <linux/slab.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+struct clk *mtk_clk_register_pll(
+ const char *name,
+ const char *parent_name,
+ u32 *base_addr,
+ u32 *pwr_addr,
+ u32 en_mask,
+ u32 flags,
+ const struct clk_ops *ops,
+ spinlock_t *lock)
+{
+ struct mtk_clk_pll *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ pr_debug("%s(): name: %s\n", __func__, name);
+
+ if (!lock)
+ return ERR_PTR(-EINVAL);
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base_addr = base_addr;
+ pll->pwr_addr = pwr_addr;
+ pll->en_mask = en_mask;
+ pll->flags = flags;
+ pll->lock = lock;
+ pll->hw.init = &init;
+
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &pll->hw);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
new file mode 100644
index 0000000..341d2fe
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pll.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __DRV_CLK_PLL_H
+#define __DRV_CLK_PLL_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+struct mtk_clk_pll {
+ struct clk_hw hw;
+ void __iomem *base_addr;
+ void __iomem *pwr_addr;
+ u32 en_mask;
+ u32 flags;
+ spinlock_t *lock;
+};
+
+#define to_mtk_clk_pll(_hw) container_of(_hw, struct mtk_clk_pll, hw)
+
+#define HAVE_RST_BAR BIT(0)
+#define HAVE_PLL_HP BIT(1)
+#define HAVE_FIX_FRQ BIT(2)
+#define PLL_AO BIT(3)
+
+struct clk *mtk_clk_register_pll(
+ const char *name,
+ const char *parent_name,
+ u32 *base_addr,
+ u32 *pwr_addr,
+ u32 en_mask,
+ u32 flags,
+ const struct clk_ops *ops,
+ spinlock_t *lock);
+
+#endif /* __DRV_CLK_PLL_H */
--
2.1.4

2015-02-09 10:47:44

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 03/13] clk: mediatek: Add reset controller support

The pericfg and infracfg units also provide reset lines to several
other SoC internal units. Add support for the reset controller.

Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mtk.h | 10 +++++
drivers/clk/mediatek/reset.c | 99 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 110 insertions(+)
create mode 100644 drivers/clk/mediatek/reset.c

diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index c384e97..0b6f1c3 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1 +1,2 @@
obj-y += clk-mtk.o clk-pll.o clk-gate.o
+obj-$(CONFIG_RESET_CONTROLLER) += reset.o
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index 35cf9a3..624863f 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -130,4 +130,14 @@ struct clk *mtk_clk_register_mux(
u8 gate_bit,
spinlock_t *lock);

+#ifdef CONFIG_RESET_CONTROLLER
+void mtk_register_reset_controller(struct device_node *np,
+ unsigned int num_regs, int regofs);
+#else
+static inline void mtk_register_reset_controller(struct device_node *np,
+ unsigned int num_regs, int regofs)
+{
+}
+#endif
+
#endif /* __DRV_CLK_MTK_H */
diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c
new file mode 100644
index 0000000..3a85a53
--- /dev/null
+++ b/drivers/clk/mediatek/reset.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+#include "clk-mtk.h"
+
+struct mtk_reset {
+ struct regmap *regmap;
+ int regofs;
+ struct reset_controller_dev rcdev;
+};
+
+static int mtk_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev);
+
+ return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2),
+ BIT(id % 32), ~0);
+}
+
+static int mtk_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev);
+
+ return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2),
+ BIT(id % 32), 0);
+}
+
+static int mtk_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = mtk_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ return mtk_reset_deassert(rcdev, id);
+}
+
+static struct reset_control_ops mtk_reset_ops = {
+ .assert = mtk_reset_assert,
+ .deassert = mtk_reset_deassert,
+ .reset = mtk_reset,
+};
+
+void mtk_register_reset_controller(struct device_node *np,
+ unsigned int num_regs, int regofs)
+{
+ struct mtk_reset *data;
+ int ret;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap)) {
+ pr_err("Cannot find regmap for %s: %ld\n", np->full_name,
+ PTR_ERR(regmap));
+ return;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->regmap = regmap;
+ data->regofs = regofs;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = num_regs * 32;
+ data->rcdev.ops = &mtk_reset_ops;
+ data->rcdev.of_node = np;
+
+ ret = reset_controller_register(&data->rcdev);
+ if (ret) {
+ pr_err("could not register reset controller: %d\n", ret);
+ kfree(data);
+ return;
+ }
+}
--
2.1.4

2015-02-09 10:48:34

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 04/13] clk: mediatek: Add basic clocks for Mediatek MT8135.

From: James Liao <[email protected]>

This patch adds basic clocks for MT8135, including TOPCKGEN, PLLs,
INFRA and PERI clocks.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8135-pll.c | 860 +++++++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-mt8135-pll.h | 28 ++
drivers/clk/mediatek/clk-mt8135.c | 873 ++++++++++++++++++++++++++++++++++
4 files changed, 1762 insertions(+)
create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.c
create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.h
create mode 100644 drivers/clk/mediatek/clk-mt8135.c

diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 0b6f1c3..afb52e5 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,2 +1,3 @@
obj-y += clk-mtk.o clk-pll.o clk-gate.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
+obj-y += clk-mt8135.o clk-mt8135-pll.o
diff --git a/drivers/clk/mediatek/clk-mt8135-pll.c b/drivers/clk/mediatek/clk-mt8135-pll.c
new file mode 100644
index 0000000..81f5a00
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8135-pll.c
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-mt8135-pll.h"
+
+#define PLL_BASE_EN BIT(0)
+#define PLL_PWR_ON BIT(0)
+#define PLL_ISO_EN BIT(1)
+#define PLL_PCW_CHG BIT(31)
+#define RST_BAR_MASK BIT(27)
+#define AUDPLL_TUNER_EN BIT(31)
+
+#define PLL_PREDIV_H 5
+#define PLL_PREDIV_L 4
+#define PLL_PREDIV_MASK GENMASK(PLL_PREDIV_H, PLL_PREDIV_L)
+#define PLL_VCODIV_L 19
+#define PLL_VCODIV_MASK BIT(19)
+
+static const u32 pll_vcodivsel_map[2] = { 1, 2 };
+static const u32 pll_prediv_map[4] = { 1, 2, 4, 4 };
+static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 };
+static const u32 pll_fbksel_map[4] = { 1, 2, 4, 4 };
+
+static u32 mtk_calc_pll_vco_freq(
+ u32 fin,
+ u32 pcw,
+ u32 vcodivsel,
+ u32 prediv,
+ u32 pcwfbits)
+{
+ /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */
+ u64 vco = fin;
+ u8 c = 0;
+
+ vco = vco * pcw * vcodivsel;
+ do_div(vco, prediv);
+
+ if (vco & GENMASK(pcwfbits - 1, 0))
+ c = 1;
+
+ vco >>= pcwfbits;
+
+ if (c)
+ ++vco;
+
+ return (u32)vco;
+}
+
+static u32 mtk_freq_limit(u32 freq)
+{
+ static const u32 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */
+ static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 62.5 MHz */
+
+ if (freq <= freq_min)
+ freq = freq_min + 16;
+ else if (freq > freq_max)
+ freq = freq_max;
+
+ return freq;
+}
+
+static int mtk_calc_pll_freq_cfg(
+ u32 *pcw,
+ u32 *postdiv_idx,
+ u32 freq,
+ u32 fin,
+ int pcwfbits)
+{
+ static const u64 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */
+ static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */
+ static const u64 postdiv[] = { 1, 2, 4, 8, 16 };
+ u64 n_info;
+ u32 idx;
+
+ /* search suitable postdiv */
+ for (idx = 0;
+ idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min;
+ idx++)
+ ;
+
+ if (idx >= ARRAY_SIZE(postdiv))
+ return -EINVAL; /* freq is out of range (too low) */
+ else if (postdiv[idx] * freq > freq_max)
+ return -EINVAL; /* freq is out of range (too high) */
+
+ /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */
+ n_info = (postdiv[idx] * freq) << pcwfbits;
+ do_div(n_info, fin);
+
+ *postdiv_idx = idx;
+ *pcw = (u32)n_info;
+
+ return 0;
+}
+
+static int mtk_clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ return (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0;
+}
+
+static int mtk_clk_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+#define SDM_PLL_POSTDIV_H 8
+#define SDM_PLL_POSTDIV_L 6
+#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L)
+#define SDM_PLL_PCW_H 20
+#define SDM_PLL_PCW_L 0
+#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L)
+
+static unsigned long mtk_clk_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L;
+ u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L;
+ u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ vcodivsel = pll_vcodivsel_map[vcodivsel];
+ prediv = pll_prediv_map[prediv];
+
+ vco_freq = mtk_calc_pll_vco_freq(
+ parent_rate, pcw, vcodivsel, prediv, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~SDM_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << SDM_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~SDM_PLL_PCW_MASK;
+ con1 |= pcw << SDM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int mtk_clk_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8135_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_pll_recalc_rate,
+ .round_rate = mtk_clk_pll_round_rate,
+ .set_rate = mtk_clk_pll_set_rate,
+};
+
+#define ARM_PLL_POSTDIV_H 26
+#define ARM_PLL_POSTDIV_L 24
+#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L)
+#define ARM_PLL_PCW_H 20
+#define ARM_PLL_PCW_L 0
+#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L)
+
+static unsigned long mtk_clk_arm_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L;
+ u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L;
+ u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ vcodivsel = pll_vcodivsel_map[vcodivsel];
+ prediv = pll_prediv_map[prediv];
+
+ vco_freq = mtk_calc_pll_vco_freq(
+ parent_rate, pcw, vcodivsel, prediv, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_arm_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con1 &= ~ARM_PLL_POSTDIV_MASK;
+ con1 |= postdiv_idx << ARM_PLL_POSTDIV_L;
+
+ /* pcw */
+ con1 &= ~ARM_PLL_PCW_MASK;
+ con1 |= pcw << ARM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int mtk_clk_arm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8135_arm_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_arm_pll_recalc_rate,
+ .round_rate = mtk_clk_pll_round_rate,
+ .set_rate = mtk_clk_arm_pll_set_rate,
+};
+
+static int mtk_clk_lc_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_lc_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+#define LC_PLL_FBKSEL_H 21
+#define LC_PLL_FBKSEL_L 20
+#define LC_PLL_FBKSEL_MASK GENMASK(LC_PLL_FBKSEL_H, LC_PLL_FBKSEL_L)
+#define LC_PLL_POSTDIV_H 8
+#define LC_PLL_POSTDIV_L 6
+#define LC_PLL_POSTDIV_MASK GENMASK(LC_PLL_POSTDIV_H, LC_PLL_POSTDIV_L)
+#define LC_PLL_FBKDIV_H 15
+#define LC_PLL_FBKDIV_L 9
+#define LC_PLL_FBKDIV_MASK GENMASK(LC_PLL_FBKDIV_H, LC_PLL_FBKDIV_L)
+
+static unsigned long mtk_clk_lc_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+
+ u32 fbksel = (con0 & LC_PLL_FBKSEL_MASK) >> LC_PLL_FBKSEL_L;
+ u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L;
+ u32 fbkdiv = (con0 & LC_PLL_FBKDIV_MASK) >> LC_PLL_FBKDIV_L;
+ u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L;
+ u32 posdiv = (con0 & LC_PLL_POSTDIV_MASK) >> LC_PLL_POSTDIV_L;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ vcodivsel = pll_vcodivsel_map[vcodivsel];
+ fbksel = pll_fbksel_map[fbksel];
+ prediv = pll_prediv_map[prediv];
+
+ vco_freq = parent_rate * fbkdiv * fbksel * vcodivsel / prediv;
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_lc_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ u32 con0;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con0 &= ~LC_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << LC_PLL_POSTDIV_L;
+
+ /* fkbdiv */
+ con0 &= ~LC_PLL_FBKDIV_MASK;
+ con0 |= pcw << LC_PLL_FBKDIV_L;
+
+ writel_relaxed(con0, con0_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_lc_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int mtk_clk_lc_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_lc_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8135_lc_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_lc_pll_prepare,
+ .unprepare = mtk_clk_lc_pll_unprepare,
+ .recalc_rate = mtk_clk_lc_pll_recalc_rate,
+ .round_rate = mtk_clk_lc_pll_round_rate,
+ .set_rate = mtk_clk_lc_pll_set_rate,
+};
+
+static int mtk_clk_aud_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con4_addr = pll->base_addr + 16;
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(con0_addr) | pll->en_mask;
+ writel_relaxed(r, con0_addr);
+
+ r = readl_relaxed(con4_addr) | AUDPLL_TUNER_EN;
+ writel_relaxed(r, con4_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(con0_addr) | RST_BAR_MASK;
+ writel_relaxed(r, con0_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_aud_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con4_addr = pll->base_addr + 16;
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(con0_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, con0_addr);
+ }
+
+ r = readl_relaxed(con4_addr) & ~AUDPLL_TUNER_EN;
+ writel_relaxed(r, con4_addr);
+
+ r = readl_relaxed(con0_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, con0_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+#define AUD_PLL_POSTDIV_H 8
+#define AUD_PLL_POSTDIV_L 6
+#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L)
+#define AUD_PLL_PCW_H 30
+#define AUD_PLL_PCW_L 0
+#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L)
+
+static unsigned long mtk_clk_aud_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L;
+ u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L;
+ u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L;
+ u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L;
+ u32 pcwfbits = 24;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ vcodivsel = pll_vcodivsel_map[vcodivsel];
+ prediv = pll_prediv_map[prediv];
+
+ vco_freq = mtk_calc_pll_vco_freq(
+ parent_rate, pcw, vcodivsel, prediv, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_aud_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ void __iomem *con4_addr = pll->base_addr + 16;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~AUD_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << AUD_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~AUD_PLL_PCW_MASK;
+ con1 |= pcw << AUD_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+ writel_relaxed(con1 + 1, con4_addr);
+ /* AUDPLL_CON4[30:0] (AUDPLL_TUNER_N_INFO) = (pcw + 1) */
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_aud_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int mtk_clk_aud_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8135_aud_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_aud_pll_prepare,
+ .unprepare = mtk_clk_aud_pll_unprepare,
+ .recalc_rate = mtk_clk_aud_pll_recalc_rate,
+ .round_rate = mtk_clk_aud_pll_round_rate,
+ .set_rate = mtk_clk_aud_pll_set_rate,
+};
+
+#define TVD_PLL_POSTDIV_H 8
+#define TVD_PLL_POSTDIV_L 6
+#define TVD_PLL_POSTDIV_MASK GENMASK(TVD_PLL_POSTDIV_H, TVD_PLL_POSTDIV_L)
+#define TVD_PLL_PCW_H 30
+#define TVD_PLL_PCW_L 0
+#define TVD_PLL_PCW_MASK GENMASK(TVD_PLL_PCW_H, TVD_PLL_PCW_L)
+
+static void mtk_clk_tvd_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~TVD_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << TVD_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~TVD_PLL_PCW_MASK;
+ con1 |= pcw << TVD_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int mtk_clk_tvd_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_tvd_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8135_tvd_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_aud_pll_recalc_rate,
+ .round_rate = mtk_clk_aud_pll_round_rate,
+ .set_rate = mtk_clk_tvd_pll_set_rate,
+};
diff --git a/drivers/clk/mediatek/clk-mt8135-pll.h b/drivers/clk/mediatek/clk-mt8135-pll.h
new file mode 100644
index 0000000..dba18e08
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8135-pll.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __DRV_CLK_MT8135_PLL_H
+#define __DRV_CLK_MT8135_PLL_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+extern const struct clk_ops mt8135_pll_ops;
+extern const struct clk_ops mt8135_arm_pll_ops;
+extern const struct clk_ops mt8135_lc_pll_ops;
+extern const struct clk_ops mt8135_aud_pll_ops;
+extern const struct clk_ops mt8135_tvd_pll_ops;
+
+#endif /* __DRV_CLK_MT8135_PLL_H */
diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c
new file mode 100644
index 0000000..93e5f02
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8135.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-gate.h"
+#include "clk-mt8135-pll.h"
+
+#include <dt-bindings/clock/mt8135-clk.h>
+
+/* ROOT */
+#define clk_null "clk_null"
+#define clk26m "clk26m"
+#define rtc32k "rtc32k"
+
+#define dsi0_lntc_dsiclk "dsi0_lntc_dsi"
+#define hdmitx_clkdig_cts "hdmitx_dig_cts"
+#define clkph_mck "clkph_mck"
+#define cpum_tck_in "cpum_tck_in"
+
+/* PLL */
+#define armpll1 "armpll1"
+#define armpll2 "armpll2"
+#define mainpll "mainpll"
+#define univpll "univpll"
+#define mmpll "mmpll"
+#define msdcpll "msdcpll"
+#define tvdpll "tvdpll"
+#define lvdspll "lvdspll"
+#define audpll "audpll"
+#define vdecpll "vdecpll"
+
+#define mainpll_806m "mainpll_806m"
+#define mainpll_537p3m "mainpll_537p3m"
+#define mainpll_322p4m "mainpll_322p4m"
+#define mainpll_230p3m "mainpll_230p3m"
+
+#define univpll_624m "univpll_624m"
+#define univpll_416m "univpll_416m"
+#define univpll_249p6m "univpll_249p6m"
+#define univpll_178p3m "univpll_178p3m"
+#define univpll_48m "univpll_48m"
+
+/* DIV */
+#define mmpll_d2 "mmpll_d2"
+#define mmpll_d3 "mmpll_d3"
+#define mmpll_d5 "mmpll_d5"
+#define mmpll_d7 "mmpll_d7"
+#define mmpll_d4 "mmpll_d4"
+#define mmpll_d6 "mmpll_d6"
+
+#define syspll_d2 "syspll_d2"
+#define syspll_d4 "syspll_d4"
+#define syspll_d6 "syspll_d6"
+#define syspll_d8 "syspll_d8"
+#define syspll_d10 "syspll_d10"
+#define syspll_d12 "syspll_d12"
+#define syspll_d16 "syspll_d16"
+#define syspll_d24 "syspll_d24"
+#define syspll_d3 "syspll_d3"
+#define syspll_d2p5 "syspll_d2p5"
+#define syspll_d5 "syspll_d5"
+#define syspll_d3p5 "syspll_d3p5"
+
+#define univpll1_d2 "univpll1_d2"
+#define univpll1_d4 "univpll1_d4"
+#define univpll1_d6 "univpll1_d6"
+#define univpll1_d8 "univpll1_d8"
+#define univpll1_d10 "univpll1_d10"
+
+#define univpll2_d2 "univpll2_d2"
+#define univpll2_d4 "univpll2_d4"
+#define univpll2_d6 "univpll2_d6"
+#define univpll2_d8 "univpll2_d8"
+
+#define univpll_d3 "univpll_d3"
+#define univpll_d5 "univpll_d5"
+#define univpll_d7 "univpll_d7"
+#define univpll_d10 "univpll_d10"
+#define univpll_d26 "univpll_d26"
+
+#define apll_ck "apll"
+#define apll_d4 "apll_d4"
+#define apll_d8 "apll_d8"
+#define apll_d16 "apll_d16"
+#define apll_d24 "apll_d24"
+
+#define lvdspll_d2 "lvdspll_d2"
+#define lvdspll_d4 "lvdspll_d4"
+#define lvdspll_d8 "lvdspll_d8"
+
+#define lvdstx_clkdig_cts "lvdstx_dig_cts"
+#define vpll_dpix_ck "vpll_dpix_ck"
+#define tvhdmi_h_ck "tvhdmi_h_ck"
+#define hdmitx_clkdig_d2 "hdmitx_dig_d2"
+#define hdmitx_clkdig_d3 "hdmitx_dig_d3"
+#define tvhdmi_d2 "tvhdmi_d2"
+#define tvhdmi_d4 "tvhdmi_d4"
+#define mempll_mck_d4 "mempll_mck_d4"
+
+/* TOP */
+#define axi_sel "axi_sel"
+#define smi_sel "smi_sel"
+#define mfg_sel "mfg_sel"
+#define irda_sel "irda_sel"
+#define cam_sel "cam_sel"
+#define aud_intbus_sel "aud_intbus_sel"
+#define jpg_sel "jpg_sel"
+#define disp_sel "disp_sel"
+#define msdc30_1_sel "msdc30_1_sel"
+#define msdc30_2_sel "msdc30_2_sel"
+#define msdc30_3_sel "msdc30_3_sel"
+#define msdc30_4_sel "msdc30_4_sel"
+#define usb20_sel "usb20_sel"
+#define venc_sel "venc_sel"
+#define spi_sel "spi_sel"
+#define uart_sel "uart_sel"
+#define mem_sel "mem_sel"
+#define camtg_sel "camtg_sel"
+#define audio_sel "audio_sel"
+#define fix_sel "fix_sel"
+#define vdec_sel "vdec_sel"
+#define ddrphycfg_sel "ddrphycfg_sel"
+#define dpilvds_sel "dpilvds_sel"
+#define pmicspi_sel "pmicspi_sel"
+#define msdc30_0_sel "msdc30_0_sel"
+#define smi_mfg_as_sel "smi_mfg_as_sel"
+#define gcpu_sel "gcpu_sel"
+#define dpi1_sel "dpi1_sel"
+#define cci_sel "cci_sel"
+#define apll_sel "apll_sel"
+#define hdmipll_sel "hdmipll_sel"
+
+/* PERI0 */
+#define i2c5_ck "i2c5_ck"
+#define i2c4_ck "i2c4_ck"
+#define i2c3_ck "i2c3_ck"
+#define i2c2_ck "i2c2_ck"
+#define i2c1_ck "i2c1_ck"
+#define i2c0_ck "i2c0_ck"
+#define uart3_ck "uart3_ck"
+#define uart2_ck "uart2_ck"
+#define uart1_ck "uart1_ck"
+#define uart0_ck "uart0_ck"
+#define irda_ck "irda_ck"
+#define nli_ck "nli_ck"
+#define md_hif_ck "md_hif_ck"
+#define ap_hif_ck "ap_hif_ck"
+#define msdc30_3_ck "msdc30_3_ck"
+#define msdc30_2_ck "msdc30_2_ck"
+#define msdc30_1_ck "msdc30_1_ck"
+#define msdc20_2_ck "msdc20_2_ck"
+#define msdc20_1_ck "msdc20_1_ck"
+#define ap_dma_ck "ap_dma_ck"
+#define usb1_ck "usb1_ck"
+#define usb0_ck "usb0_ck"
+#define pwm_ck "pwm_ck"
+#define pwm7_ck "pwm7_ck"
+#define pwm6_ck "pwm6_ck"
+#define pwm5_ck "pwm5_ck"
+#define pwm4_ck "pwm4_ck"
+#define pwm3_ck "pwm3_ck"
+#define pwm2_ck "pwm2_ck"
+#define pwm1_ck "pwm1_ck"
+#define therm_ck "therm_ck"
+#define nfi_ck "nfi_ck"
+
+/* PERI1 */
+#define usbslv_ck "usbslv_ck"
+#define usb1_mcu_ck "usb1_mcu_ck"
+#define usb0_mcu_ck "usb0_mcu_ck"
+#define gcpu_ck "gcpu_ck"
+#define fhctl_ck "fhctl_ck"
+#define spi1_ck "spi1_ck"
+#define auxadc_ck "auxadc_ck"
+#define peri_pwrap_ck "peri_pwrap_ck"
+#define i2c6_ck "i2c6_ck"
+
+/* INFRA */
+#define pmic_wrap_ck "pmic_wrap_ck"
+#define pmicspi_ck "pmicspi_ck"
+#define ccif1_ap_ctrl "ccif1_ap_ctrl"
+#define ccif0_ap_ctrl "ccif0_ap_ctrl"
+#define kp_ck "kp_ck"
+#define cpum_ck "cpum_ck"
+#define m4u_ck "m4u_ck"
+#define mfgaxi_ck "mfgaxi_ck"
+#define devapc_ck "devapc_ck"
+#define audio_ck "audio_ck"
+#define mfg_bus_ck "mfg_bus_ck"
+#define smi_ck "smi_ck"
+#define dbgclk_ck "dbgclk_ck"
+
+static DEFINE_SPINLOCK(lock);
+
+static struct mtk_fixed_factor root_clk_alias[] __initdata = {
+ FACTOR(TOP_DSI0_LNTC_DSICLK, dsi0_lntc_dsiclk, clk_null, 1, 1),
+ FACTOR(TOP_HDMITX_CLKDIG_CTS, hdmitx_clkdig_cts, clk_null, 1, 1),
+ FACTOR(TOP_CLKPH_MCK, clkph_mck, clk_null, 1, 1),
+ FACTOR(TOP_CPUM_TCK_IN, cpum_tck_in, clk_null, 1, 1),
+};
+
+static struct mtk_fixed_factor top_divs[] __initdata = {
+ FACTOR(TOP_MAINPLL_806M, mainpll_806m, mainpll, 1, 2),
+ FACTOR(TOP_MAINPLL_537P3M, mainpll_537p3m, mainpll, 1, 3),
+ FACTOR(TOP_MAINPLL_322P4M, mainpll_322p4m, mainpll, 1, 5),
+ FACTOR(TOP_MAINPLL_230P3M, mainpll_230p3m, mainpll, 1, 7),
+
+ FACTOR(TOP_UNIVPLL_624M, univpll_624m, univpll, 1, 2),
+ FACTOR(TOP_UNIVPLL_416M, univpll_416m, univpll, 1, 3),
+ FACTOR(TOP_UNIVPLL_249P6M, univpll_249p6m, univpll, 1, 5),
+ FACTOR(TOP_UNIVPLL_178P3M, univpll_178p3m, univpll, 1, 7),
+ FACTOR(TOP_UNIVPLL_48M, univpll_48m, univpll, 1, 26),
+
+ FACTOR(TOP_MMPLL_D2, mmpll_d2, mmpll, 1, 2),
+ FACTOR(TOP_MMPLL_D3, mmpll_d3, mmpll, 1, 3),
+ FACTOR(TOP_MMPLL_D5, mmpll_d5, mmpll, 1, 5),
+ FACTOR(TOP_MMPLL_D7, mmpll_d7, mmpll, 1, 7),
+ FACTOR(TOP_MMPLL_D4, mmpll_d4, mmpll_d2, 1, 2),
+ FACTOR(TOP_MMPLL_D6, mmpll_d6, mmpll_d3, 1, 2),
+
+ FACTOR(TOP_SYSPLL_D2, syspll_d2, mainpll_806m, 1, 1),
+ FACTOR(TOP_SYSPLL_D4, syspll_d4, mainpll_806m, 1, 2),
+ FACTOR(TOP_SYSPLL_D6, syspll_d6, mainpll_806m, 1, 3),
+ FACTOR(TOP_SYSPLL_D8, syspll_d8, mainpll_806m, 1, 4),
+ FACTOR(TOP_SYSPLL_D10, syspll_d10, mainpll_806m, 1, 5),
+ FACTOR(TOP_SYSPLL_D12, syspll_d12, mainpll_806m, 1, 6),
+ FACTOR(TOP_SYSPLL_D16, syspll_d16, mainpll_806m, 1, 8),
+ FACTOR(TOP_SYSPLL_D24, syspll_d24, mainpll_806m, 1, 12),
+
+ FACTOR(TOP_SYSPLL_D3, syspll_d3, mainpll_537p3m, 1, 1),
+
+ FACTOR(TOP_SYSPLL_D2P5, syspll_d2p5, mainpll_322p4m, 2, 1),
+ FACTOR(TOP_SYSPLL_D5, syspll_d5, mainpll_322p4m, 1, 1),
+
+ FACTOR(TOP_SYSPLL_D3P5, syspll_d3p5, mainpll_230p3m, 2, 1),
+
+ FACTOR(TOP_UNIVPLL1_D2, univpll1_d2, univpll_624m, 1, 2),
+ FACTOR(TOP_UNIVPLL1_D4, univpll1_d4, univpll_624m, 1, 4),
+ FACTOR(TOP_UNIVPLL1_D6, univpll1_d6, univpll_624m, 1, 6),
+ FACTOR(TOP_UNIVPLL1_D8, univpll1_d8, univpll_624m, 1, 8),
+ FACTOR(TOP_UNIVPLL1_D10, univpll1_d10, univpll_624m, 1, 10),
+
+ FACTOR(TOP_UNIVPLL2_D2, univpll2_d2, univpll_416m, 1, 2),
+ FACTOR(TOP_UNIVPLL2_D4, univpll2_d4, univpll_416m, 1, 4),
+ FACTOR(TOP_UNIVPLL2_D6, univpll2_d6, univpll_416m, 1, 6),
+ FACTOR(TOP_UNIVPLL2_D8, univpll2_d8, univpll_416m, 1, 8),
+
+ FACTOR(TOP_UNIVPLL_D3, univpll_d3, univpll_416m, 1, 1),
+ FACTOR(TOP_UNIVPLL_D5, univpll_d5, univpll_249p6m, 1, 1),
+ FACTOR(TOP_UNIVPLL_D7, univpll_d7, univpll_178p3m, 1, 1),
+ FACTOR(TOP_UNIVPLL_D10, univpll_d10, univpll_249p6m, 1, 5),
+ FACTOR(TOP_UNIVPLL_D26, univpll_d26, univpll_48m, 1, 1),
+
+ FACTOR(TOP_APLL_CK, apll_ck, audpll, 1, 1),
+ FACTOR(TOP_APLL_D4, apll_d4, audpll, 1, 4),
+ FACTOR(TOP_APLL_D8, apll_d8, audpll, 1, 8),
+ FACTOR(TOP_APLL_D16, apll_d16, audpll, 1, 16),
+ FACTOR(TOP_APLL_D24, apll_d24, audpll, 1, 24),
+
+ FACTOR(TOP_LVDSPLL_D2, lvdspll_d2, lvdspll, 1, 2),
+ FACTOR(TOP_LVDSPLL_D4, lvdspll_d4, lvdspll, 1, 4),
+ FACTOR(TOP_LVDSPLL_D8, lvdspll_d8, lvdspll, 1, 8),
+
+ FACTOR(TOP_LVDSTX_CLKDIG_CT, lvdstx_clkdig_cts, lvdspll, 1, 1),
+ FACTOR(TOP_VPLL_DPIX_CK, vpll_dpix_ck, lvdspll, 1, 1),
+
+ FACTOR(TOP_TVHDMI_H_CK, tvhdmi_h_ck, tvdpll, 1, 1),
+
+ FACTOR(TOP_HDMITX_CLKDIG_D2, hdmitx_clkdig_d2, hdmitx_clkdig_cts, 1, 2),
+ FACTOR(TOP_HDMITX_CLKDIG_D3, hdmitx_clkdig_d3, hdmitx_clkdig_cts, 1, 3),
+
+ FACTOR(TOP_TVHDMI_D2, tvhdmi_d2, tvhdmi_h_ck, 1, 2),
+ FACTOR(TOP_TVHDMI_D4, tvhdmi_d4, tvhdmi_h_ck, 1, 4),
+
+ FACTOR(TOP_MEMPLL_MCK_D4, mempll_mck_d4, clkph_mck, 1, 4),
+};
+
+static const char *axi_parents[] __initconst = {
+ clk26m,
+ syspll_d3,
+ syspll_d4,
+ syspll_d6,
+ univpll_d5,
+ univpll2_d2,
+ syspll_d3p5};
+
+static const char *smi_parents[] __initconst = {
+ clk26m,
+ clkph_mck,
+ syspll_d2p5,
+ syspll_d3,
+ syspll_d8,
+ univpll_d5,
+ univpll1_d2,
+ univpll1_d6,
+ mmpll_d3,
+ mmpll_d4,
+ mmpll_d5,
+ mmpll_d6,
+ mmpll_d7,
+ vdecpll,
+ lvdspll};
+
+static const char *mfg_parents[] __initconst = {
+ clk26m,
+ univpll1_d4,
+ syspll_d2,
+ syspll_d2p5,
+ syspll_d3,
+ univpll_d5,
+ univpll1_d2,
+ mmpll_d2,
+ mmpll_d3,
+ mmpll_d4,
+ mmpll_d5,
+ mmpll_d6,
+ mmpll_d7};
+
+static const char *irda_parents[] __initconst = {
+ clk26m,
+ univpll2_d8,
+ univpll1_d6};
+
+static const char *cam_parents[] __initconst = {
+ clk26m,
+ syspll_d3,
+ syspll_d3p5,
+ syspll_d4,
+ univpll_d5,
+ univpll2_d2,
+ univpll_d7,
+ univpll1_d4};
+
+static const char *aud_intbus_parents[] __initconst = {
+ clk26m,
+ syspll_d6,
+ univpll_d10};
+
+static const char *jpg_parents[] __initconst = {
+ clk26m,
+ syspll_d5,
+ syspll_d4,
+ syspll_d3,
+ univpll_d7,
+ univpll2_d2,
+ univpll_d5};
+
+static const char *disp_parents[] __initconst = {
+ clk26m,
+ syspll_d3p5,
+ syspll_d3,
+ univpll2_d2,
+ univpll_d5,
+ univpll1_d2,
+ lvdspll,
+ vdecpll};
+
+static const char *msdc30_parents[] __initconst = {
+ clk26m,
+ syspll_d6,
+ syspll_d5,
+ univpll1_d4,
+ univpll2_d4,
+ msdcpll};
+
+static const char *usb20_parents[] __initconst = {
+ clk26m,
+ univpll2_d6,
+ univpll1_d10};
+
+static const char *venc_parents[] __initconst = {
+ clk26m,
+ syspll_d3,
+ syspll_d8,
+ univpll_d5,
+ univpll1_d6,
+ mmpll_d4,
+ mmpll_d5,
+ mmpll_d6};
+
+static const char *spi_parents[] __initconst = {
+ clk26m,
+ syspll_d6,
+ syspll_d8,
+ syspll_d10,
+ univpll1_d6,
+ univpll1_d8};
+
+static const char *uart_parents[] __initconst = {
+ clk26m,
+ univpll2_d8};
+
+static const char *mem_parents[] __initconst = {
+ clk26m,
+ clkph_mck};
+
+static const char *camtg_parents[] __initconst = {
+ clk26m,
+ univpll_d26,
+ univpll1_d6,
+ syspll_d16,
+ syspll_d8};
+
+static const char *audio_parents[] __initconst = {
+ clk26m,
+ syspll_d24};
+
+static const char *fix_parents[] __initconst = {
+ rtc32k,
+ clk26m,
+ univpll_d5,
+ univpll_d7,
+ univpll1_d2,
+ univpll1_d4,
+ univpll1_d6,
+ univpll1_d8};
+
+static const char *vdec_parents[] __initconst = {
+ clk26m,
+ vdecpll,
+ clkph_mck,
+ syspll_d2p5,
+ syspll_d3,
+ syspll_d3p5,
+ syspll_d4,
+ syspll_d5,
+ syspll_d6,
+ syspll_d8,
+ univpll1_d2,
+ univpll2_d2,
+ univpll_d7,
+ univpll_d10,
+ univpll2_d4,
+ lvdspll};
+
+static const char *ddrphycfg_parents[] __initconst = {
+ clk26m,
+ axi_sel,
+ syspll_d12};
+
+static const char *dpilvds_parents[] __initconst = {
+ clk26m,
+ lvdspll,
+ lvdspll_d2,
+ lvdspll_d4,
+ lvdspll_d8};
+
+static const char *pmicspi_parents[] __initconst = {
+ clk26m,
+ univpll2_d6,
+ syspll_d8,
+ syspll_d10,
+ univpll1_d10,
+ mempll_mck_d4,
+ univpll_d26,
+ syspll_d24};
+
+static const char *smi_mfg_as_parents[] __initconst = {
+ clk26m,
+ smi_sel,
+ mfg_sel,
+ mem_sel};
+
+static const char *gcpu_parents[] __initconst = {
+ clk26m,
+ syspll_d4,
+ univpll_d7,
+ syspll_d5,
+ syspll_d6};
+
+static const char *dpi1_parents[] __initconst = {
+ clk26m,
+ tvhdmi_h_ck,
+ tvhdmi_d2,
+ tvhdmi_d4};
+
+static const char *cci_parents[] __initconst = {
+ clk26m,
+ mainpll_537p3m,
+ univpll_d3,
+ syspll_d2p5,
+ syspll_d3,
+ syspll_d5};
+
+static const char *apll_parents[] __initconst = {
+ clk26m,
+ apll_ck,
+ apll_d4,
+ apll_d8,
+ apll_d16,
+ apll_d24};
+
+static const char *hdmipll_parents[] __initconst = {
+ clk26m,
+ hdmitx_clkdig_cts,
+ hdmitx_clkdig_d2,
+ hdmitx_clkdig_d3};
+
+static struct mtk_mux top_muxes[] __initdata = {
+ /* CLK_CFG_0 */
+ MUX(TOP_AXI_SEL, axi_sel, axi_parents,
+ 0x0140, 0, 3, INVALID_MUX_GATE_BIT),
+ MUX(TOP_SMI_SEL, smi_sel, smi_parents, 0x0140, 8, 4, 15),
+ MUX(TOP_MFG_SEL, mfg_sel, mfg_parents, 0x0140, 16, 4, 23),
+ MUX(TOP_IRDA_SEL, irda_sel, irda_parents, 0x0140, 24, 2, 31),
+ /* CLK_CFG_1 */
+ MUX(TOP_CAM_SEL, cam_sel, cam_parents, 0x0144, 0, 3, 7),
+ MUX(TOP_AUD_INTBUS_SEL, aud_intbus_sel, aud_intbus_parents,
+ 0x0144, 8, 2, 15),
+ MUX(TOP_JPG_SEL, jpg_sel, jpg_parents, 0x0144, 16, 3, 23),
+ MUX(TOP_DISP_SEL, disp_sel, disp_parents, 0x0144, 24, 3, 31),
+ /* CLK_CFG_2 */
+ MUX(TOP_MSDC30_1_SEL, msdc30_1_sel, msdc30_parents, 0x0148, 0, 3, 7),
+ MUX(TOP_MSDC30_2_SEL, msdc30_2_sel, msdc30_parents, 0x0148, 8, 3, 15),
+ MUX(TOP_MSDC30_3_SEL, msdc30_3_sel, msdc30_parents, 0x0148, 16, 3, 23),
+ MUX(TOP_MSDC30_4_SEL, msdc30_4_sel, msdc30_parents, 0x0148, 24, 3, 31),
+ /* CLK_CFG_3 */
+ MUX(TOP_USB20_SEL, usb20_sel, usb20_parents, 0x014c, 0, 2, 7),
+ /* CLK_CFG_4 */
+ MUX(TOP_VENC_SEL, venc_sel, venc_parents, 0x0150, 8, 3, 15),
+ MUX(TOP_SPI_SEL, spi_sel, spi_parents, 0x0150, 16, 3, 23),
+ MUX(TOP_UART_SEL, uart_sel, uart_parents, 0x0150, 24, 2, 31),
+ /* CLK_CFG_6 */
+ MUX(TOP_MEM_SEL, mem_sel, mem_parents, 0x0158, 0, 2, 7),
+ MUX(TOP_CAMTG_SEL, camtg_sel, camtg_parents, 0x0158, 8, 3, 15),
+ MUX(TOP_AUDIO_SEL, audio_sel, audio_parents, 0x0158, 24, 2, 31),
+ /* CLK_CFG_7 */
+ MUX(TOP_FIX_SEL, fix_sel, fix_parents, 0x015c, 0, 3, 7),
+ MUX(TOP_VDEC_SEL, vdec_sel, vdec_parents, 0x015c, 8, 4, 15),
+ MUX(TOP_DDRPHYCFG_SEL, ddrphycfg_sel, ddrphycfg_parents,
+ 0x015c, 16, 2, 23),
+ MUX(TOP_DPILVDS_SEL, dpilvds_sel, dpilvds_parents, 0x015c, 24, 3, 31),
+ /* CLK_CFG_8 */
+ MUX(TOP_PMICSPI_SEL, pmicspi_sel, pmicspi_parents, 0x0164, 0, 3, 7),
+ MUX(TOP_MSDC30_0_SEL, msdc30_0_sel, msdc30_parents, 0x0164, 8, 3, 15),
+ MUX(TOP_SMI_MFG_AS_SEL, smi_mfg_as_sel, smi_mfg_as_parents,
+ 0x0164, 16, 2, 23),
+ MUX(TOP_GCPU_SEL, gcpu_sel, gcpu_parents, 0x0164, 24, 3, 31),
+ /* CLK_CFG_9 */
+ MUX(TOP_DPI1_SEL, dpi1_sel, dpi1_parents, 0x0168, 0, 2, 7),
+ MUX(TOP_CCI_SEL, cci_sel, cci_parents, 0x0168, 8, 3, 15),
+ MUX(TOP_APLL_SEL, apll_sel, apll_parents, 0x0168, 16, 3, 23),
+ MUX(TOP_HDMIPLL_SEL, hdmipll_sel, hdmipll_parents, 0x0168, 24, 2, 31),
+};
+
+static void __init mtk_init_clk_topckgen(void __iomem *top_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(top_muxes); i++) {
+ struct mtk_mux *mux = &top_muxes[i];
+
+ clk = mtk_clk_register_mux(mux->name,
+ mux->parent_names, mux->num_parents,
+ top_base + mux->reg, mux->shift, mux->width,
+ mux->gate, &lock);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ mux->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[mux->id] = clk;
+ }
+}
+
+static struct mtk_pll plls[] __initdata = {
+ PLL(APMIXED_ARMPLL1, armpll1, clk26m, 0x0200, 0x0218,
+ 0x80000001, HAVE_PLL_HP, &mt8135_arm_pll_ops),
+ PLL(APMIXED_ARMPLL2, armpll2, clk26m, 0x02cc, 0x02e4,
+ 0x80000001, HAVE_PLL_HP, &mt8135_arm_pll_ops),
+ PLL(APMIXED_MAINPLL, mainpll, clk26m, 0x021c, 0x0234,
+ 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR | PLL_AO,
+ &mt8135_pll_ops),
+ PLL(APMIXED_UNIVPLL, univpll, clk26m, 0x0238, 0x0250,
+ 0xf3000001, HAVE_RST_BAR | HAVE_FIX_FRQ | PLL_AO,
+ &mt8135_lc_pll_ops),
+ PLL(APMIXED_MMPLL, mmpll, clk26m, 0x0254, 0x026c,
+ 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR, &mt8135_pll_ops),
+ PLL(APMIXED_MSDCPLL, msdcpll, clk26m, 0x0278, 0x0290,
+ 0x80000001, HAVE_PLL_HP, &mt8135_pll_ops),
+ PLL(APMIXED_TVDPLL, tvdpll, clk26m, 0x0294, 0x02ac,
+ 0x80000001, HAVE_PLL_HP, &mt8135_tvd_pll_ops),
+ PLL(APMIXED_LVDSPLL, lvdspll, clk26m, 0x02b0, 0x02c8,
+ 0x80000001, HAVE_PLL_HP, &mt8135_pll_ops),
+ PLL(APMIXED_AUDPLL, audpll, clk26m, 0x02e8, 0x0300,
+ 0x80000001, 0, &mt8135_aud_pll_ops),
+ PLL(APMIXED_VDECPLL, vdecpll, clk26m, 0x0304, 0x031c,
+ 0x80000001, HAVE_PLL_HP, &mt8135_pll_ops),
+};
+
+static void __init mtk_init_clk_apmixedsys(void __iomem *apmixed_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(plls); i++) {
+ struct mtk_pll *pll = &plls[i];
+
+ clk = mtk_clk_register_pll(pll->name, pll->parent_name,
+ apmixed_base + pll->reg,
+ apmixed_base + pll->pwr_reg,
+ pll->en_mask, pll->flags, pll->ops, &lock);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ pll->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[pll->id] = clk;
+ }
+}
+
+static struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x0040,
+ .clr_ofs = 0x0044,
+ .sta_ofs = 0x0048,
+};
+
+static struct mtk_gate infra_clks[] __initdata = {
+ GATE(INFRA_PMIC_WRAP_CK, pmic_wrap_ck, axi_sel, infra_cg_regs,
+ 23, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_PMICSPI_CK, pmicspi_ck, pmicspi_sel, infra_cg_regs,
+ 22, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_CCIF1_AP_CTRL, ccif1_ap_ctrl, axi_sel, infra_cg_regs,
+ 21, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_CCIF0_AP_CTRL, ccif0_ap_ctrl, axi_sel, infra_cg_regs,
+ 20, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_KP_CK, kp_ck, axi_sel, infra_cg_regs,
+ 16, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_CPUM_CK, cpum_ck, cpum_tck_in, infra_cg_regs,
+ 15, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_M4U_CK, m4u_ck, mem_sel, infra_cg_regs,
+ 8, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_MFGAXI_CK, mfgaxi_ck, axi_sel, infra_cg_regs,
+ 7, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_DEVAPC_CK, devapc_ck, axi_sel, infra_cg_regs,
+ 6, &mtk_clk_gate_ops_setclr_inv),
+ GATE(INFRA_AUDIO_CK, audio_ck, aud_intbus_sel, infra_cg_regs,
+ 5, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_MFG_BUS_CK, mfg_bus_ck, axi_sel, infra_cg_regs,
+ 2, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_SMI_CK, smi_ck, smi_sel, infra_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_DBGCLK_CK, dbgclk_ck, axi_sel, infra_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+};
+
+static struct mtk_gate_regs peri0_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x0010,
+ .sta_ofs = 0x0018,
+};
+
+static struct mtk_gate_regs peri1_cg_regs = {
+ .set_ofs = 0x000c,
+ .clr_ofs = 0x0014,
+ .sta_ofs = 0x001c,
+};
+
+static struct mtk_gate peri_clks[] __initdata = {
+ /* PERI0 */
+ GATE(PERI_I2C5_CK, i2c5_ck, axi_sel, peri0_cg_regs,
+ 31, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C4_CK, i2c4_ck, axi_sel, peri0_cg_regs,
+ 30, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C3_CK, i2c3_ck, axi_sel, peri0_cg_regs,
+ 29, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C2_CK, i2c2_ck, axi_sel, peri0_cg_regs,
+ 28, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C1_CK, i2c1_ck, axi_sel, peri0_cg_regs,
+ 27, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C0_CK, i2c0_ck, axi_sel, peri0_cg_regs,
+ 26, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART3_CK, uart3_ck, axi_sel, peri0_cg_regs,
+ 25, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART2_CK, uart2_ck, axi_sel, peri0_cg_regs,
+ 24, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART1_CK, uart1_ck, axi_sel, peri0_cg_regs,
+ 23, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART0_CK, uart0_ck, axi_sel, peri0_cg_regs,
+ 22, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_IRDA_CK, irda_ck, irda_sel, peri0_cg_regs,
+ 21, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_NLI_CK, nli_ck, axi_sel, peri0_cg_regs,
+ 20, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MD_HIF_CK, md_hif_ck, axi_sel, peri0_cg_regs,
+ 19, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_AP_HIF_CK, ap_hif_ck, axi_sel, peri0_cg_regs,
+ 18, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_3_CK, msdc30_3_ck, msdc30_4_sel, peri0_cg_regs,
+ 17, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_2_CK, msdc30_2_ck, msdc30_3_sel, peri0_cg_regs,
+ 16, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_1_CK, msdc30_1_ck, msdc30_2_sel, peri0_cg_regs,
+ 15, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC20_2_CK, msdc20_2_ck, msdc30_1_sel, peri0_cg_regs,
+ 14, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC20_1_CK, msdc20_1_ck, msdc30_0_sel, peri0_cg_regs,
+ 13, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_AP_DMA_CK, ap_dma_ck, axi_sel, peri0_cg_regs,
+ 12, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB1_CK, usb1_ck, usb20_sel, peri0_cg_regs,
+ 11, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB0_CK, usb0_ck, usb20_sel, peri0_cg_regs,
+ 10, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM_CK, pwm_ck, axi_sel, peri0_cg_regs,
+ 9, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM7_CK, pwm7_ck, axi_sel, peri0_cg_regs,
+ 8, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM6_CK, pwm6_ck, axi_sel, peri0_cg_regs,
+ 7, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM5_CK, pwm5_ck, axi_sel, peri0_cg_regs,
+ 6, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM4_CK, pwm4_ck, axi_sel, peri0_cg_regs,
+ 5, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM3_CK, pwm3_ck, axi_sel, peri0_cg_regs,
+ 4, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM2_CK, pwm2_ck, axi_sel, peri0_cg_regs,
+ 3, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM1_CK, pwm1_ck, axi_sel, peri0_cg_regs,
+ 2, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_THERM_CK, therm_ck, axi_sel, peri0_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_NFI_CK, nfi_ck, axi_sel, peri0_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+ /* PERI1 */
+ GATE(PERI_USBSLV_CK, usbslv_ck, axi_sel, peri1_cg_regs,
+ 8, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB1_MCU_CK, usb1_mcu_ck, axi_sel, peri1_cg_regs,
+ 7, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB0_MCU_CK, usb0_mcu_ck, axi_sel, peri1_cg_regs,
+ 6, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_GCPU_CK, gcpu_ck, gcpu_sel, peri1_cg_regs,
+ 5, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_FHCTL_CK, fhctl_ck, clk26m, peri1_cg_regs,
+ 4, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_SPI1_CK, spi1_ck, spi_sel, peri1_cg_regs,
+ 3, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_AUXADC_CK, auxadc_ck, clk26m, peri1_cg_regs,
+ 2, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PERI_PWRAP_CK, peri_pwrap_ck, axi_sel, peri1_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C6_CK, i2c6_ck, axi_sel, peri1_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+};
+
+static void __init mtk_topckgen_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(TOP_NR_CLK);
+
+ mtk_init_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
+ mtk_init_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
+ mtk_init_clk_topckgen(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+}
+CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init);
+
+static void __init mtk_apmixedsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(APMIXED_NR_CLK);
+
+ mtk_init_clk_apmixedsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+}
+CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys",
+ mtk_apmixedsys_init);
+
+static void __init mtk_infrasys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ struct regmap *regmap;
+ int r;
+
+ regmap = syscon_node_to_regmap(node);
+ if (IS_ERR(regmap)) {
+ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
+ PTR_ERR(regmap));
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(INFRA_NR_CLK);
+
+ mtk_init_clk_gates(regmap, infra_clks, ARRAY_SIZE(infra_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ mtk_register_reset_controller(node, 2, 0x30);
+}
+CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init);
+
+static void __init mtk_pericfg_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ struct regmap *regmap;
+ int r;
+
+ regmap = syscon_node_to_regmap(node);
+ if (IS_ERR(regmap)) {
+ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
+ PTR_ERR(regmap));
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(PERI_NR_CLK);
+
+ mtk_init_clk_gates(regmap, peri_clks, ARRAY_SIZE(peri_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ mtk_register_reset_controller(node, 2, 0);
+}
+CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init);
--
2.1.4

2015-02-09 10:47:34

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 05/13] clk: dts: mediatek: add Mediatek MT8173 clock bindings

From: James Liao <[email protected]>

Document the device-tree binding of Mediatek MT8173 SoC, including
TOPCKGEN, PLLs, INFRA and PERI clock controller.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
.../bindings/clock/mediatek,mt8173-clock.txt | 42 ++++
include/dt-bindings/clock/mt8173-clk.h | 214 +++++++++++++++++++++
2 files changed, 256 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8173-clock.txt
create mode 100644 include/dt-bindings/clock/mt8173-clk.h

diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8173-clock.txt b/Documentation/devicetree/bindings/clock/mediatek,mt8173-clock.txt
new file mode 100644
index 0000000..15cd49a
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/mediatek,mt8173-clock.txt
@@ -0,0 +1,42 @@
+Mediatek MT8173 Clock Controller
+
+This binding uses the common clock binding:
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The Mediatek MT8173 clock controller generates and supplies clock to various
+controllers within Mediatek MT8173 SoC.
+
+Required Properties:
+
+- compatible: should be one of following:
+ - "mediatek,mt8173-topckgen" : for topckgen clock controller of MT8173.
+ - "mediatek,mt8173-apmixedsys" : for apmixed_sys (PLLs) of MT8173.
+ - "mediatek,mt8173-infrasys" : for infra_sys clock controller of MT8173.
+ - "mediatek,mt8173-pericfg" : for peri_sys clock controller of MT8173.
+
+- reg: physical base address of the controller and length of memory mapped
+ region.
+
+- #clock-cells: should be 1.
+
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/mt8173-clk.h header and can be used in device tree sources.
+
+Example: I2C controller node that consumes the clock generated by the clock
+ controller (refer to the standard clock bindings for information about
+ "clocks" and "clock-names" properties):
+
+ pericfg: pericfg@10003000 {
+ compatible = "mediatek,mt8173-pericfg";
+ reg = <0 0x10003000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ i2c0: i2c@1100d000 {
+ compatible = "mediatek,mt8173-i2c", "mediatek,mt6589-i2c";
+ reg = <0 0x1100d000 0 0x70>, <0 0x11000300 0 0x80>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_LOW>;
+ clock-div = <16>;
+ clocks = <&pericfg PERI_I2C0>, <&pericfg PERI_AP_DMA>;
+ clock-names = "main", "dma";
+ };
diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h
new file mode 100644
index 0000000..f3f1d13
--- /dev/null
+++ b/include/dt-bindings/clock/mt8173-clk.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_CLK_MT8173_H
+#define _DT_BINDINGS_CLK_MT8173_H
+
+/* TOPCKGEN */
+
+#define TOP_CLKPH_MCK_O 1
+#define TOP_DPI_CK 2
+#define TOP_USB_SYSPLL_125M 3
+#define TOP_HDMITX_DIG_CTS 4
+#define TOP_ARMCA7PLL_754M 5
+#define TOP_ARMCA7PLL_502M 6
+#define TOP_MAIN_H546M 7
+#define TOP_MAIN_H364M 8
+#define TOP_MAIN_H218P4M 9
+#define TOP_MAIN_H156M 10
+#define TOP_TVDPLL_445P5M 11
+#define TOP_TVDPLL_594M 12
+#define TOP_UNIV_624M 13
+#define TOP_UNIV_416M 14
+#define TOP_UNIV_249P6M 15
+#define TOP_UNIV_178P3M 16
+#define TOP_UNIV_48M 17
+#define TOP_CLKRTC_EXT 18
+#define TOP_CLKRTC_INT 19
+#define TOP_FPC_CK 20
+#define TOP_HDMITXPLL_D2 21
+#define TOP_HDMITXPLL_D3 22
+#define TOP_ARMCA7PLL_D2 23
+#define TOP_ARMCA7PLL_D3 24
+#define TOP_APLL1_CK 25
+#define TOP_APLL2_CK 26
+#define TOP_DMPLL_CK 27
+#define TOP_DMPLL_D2 28
+#define TOP_DMPLL_D4 29
+#define TOP_DMPLL_D8 30
+#define TOP_DMPLL_D16 31
+#define TOP_LVDSPLL_D2 32
+#define TOP_LVDSPLL_D4 33
+#define TOP_LVDSPLL_D8 34
+#define TOP_MMPLL_CK 35
+#define TOP_MMPLL_D2 36
+#define TOP_MSDCPLL_CK 37
+#define TOP_MSDCPLL_D2 38
+#define TOP_MSDCPLL_D4 39
+#define TOP_MSDCPLL2_CK 40
+#define TOP_MSDCPLL2_D2 41
+#define TOP_MSDCPLL2_D4 42
+#define TOP_SYSPLL_D2 43
+#define TOP_SYSPLL1_D2 44
+#define TOP_SYSPLL1_D4 45
+#define TOP_SYSPLL1_D8 46
+#define TOP_SYSPLL1_D16 47
+#define TOP_SYSPLL_D3 48
+#define TOP_SYSPLL2_D2 49
+#define TOP_SYSPLL2_D4 50
+#define TOP_SYSPLL_D5 51
+#define TOP_SYSPLL3_D2 52
+#define TOP_SYSPLL3_D4 53
+#define TOP_SYSPLL_D7 54
+#define TOP_SYSPLL4_D2 55
+#define TOP_SYSPLL4_D4 56
+#define TOP_TVDPLL_CK 57
+#define TOP_TVDPLL_D2 58
+#define TOP_TVDPLL_D4 59
+#define TOP_TVDPLL_D8 60
+#define TOP_TVDPLL_D16 61
+#define TOP_UNIVPLL_D2 62
+#define TOP_UNIVPLL1_D2 63
+#define TOP_UNIVPLL1_D4 64
+#define TOP_UNIVPLL1_D8 65
+#define TOP_UNIVPLL_D3 66
+#define TOP_UNIVPLL2_D2 67
+#define TOP_UNIVPLL2_D4 68
+#define TOP_UNIVPLL2_D8 69
+#define TOP_UNIVPLL_D5 70
+#define TOP_UNIVPLL3_D2 71
+#define TOP_UNIVPLL3_D4 72
+#define TOP_UNIVPLL3_D8 73
+#define TOP_UNIVPLL_D7 74
+#define TOP_UNIVPLL_D26 75
+#define TOP_UNIVPLL_D52 76
+#define TOP_VCODECPLL_CK 77
+#define TOP_VCODECPLL_370P5 78
+#define TOP_VENCPLL_CK 79
+#define TOP_VENCPLL_D2 80
+#define TOP_VENCPLL_D4 81
+#define TOP_AXI_SEL 82
+#define TOP_MEM_SEL 83
+#define TOP_DDRPHYCFG_SEL 84
+#define TOP_MM_SEL 85
+#define TOP_PWM_SEL 86
+#define TOP_VDEC_SEL 87
+#define TOP_VENC_SEL 88
+#define TOP_MFG_SEL 89
+#define TOP_CAMTG_SEL 90
+#define TOP_UART_SEL 91
+#define TOP_SPI_SEL 92
+#define TOP_USB20_SEL 93
+#define TOP_USB30_SEL 94
+#define TOP_MSDC50_0_H_SEL 95
+#define TOP_MSDC50_0_SEL 96
+#define TOP_MSDC30_1_SEL 97
+#define TOP_MSDC30_2_SEL 98
+#define TOP_MSDC30_3_SEL 99
+#define TOP_AUDIO_SEL 100
+#define TOP_AUD_INTBUS_SEL 101
+#define TOP_PMICSPI_SEL 102
+#define TOP_SCP_SEL 103
+#define TOP_ATB_SEL 104
+#define TOP_VENC_LT_SEL 105
+#define TOP_DPI0_SEL 106
+#define TOP_IRDA_SEL 107
+#define TOP_CCI400_SEL 108
+#define TOP_AUD_1_SEL 109
+#define TOP_AUD_2_SEL 110
+#define TOP_MEM_MFG_IN_SEL 111
+#define TOP_AXI_MFG_IN_SEL 112
+#define TOP_SCAM_SEL 113
+#define TOP_SPINFI_IFR_SEL 114
+#define TOP_HDMI_SEL 115
+#define TOP_DPILVDS_SEL 116
+#define TOP_MSDC50_2_H_SEL 117
+#define TOP_HDCP_SEL 118
+#define TOP_HDCP_24M_SEL 119
+#define TOP_RTC_SEL 120
+#define TOP_NR_CLK 121
+
+/* APMIXED_SYS */
+
+#define APMIXED_ARMCA15PLL 1
+#define APMIXED_ARMCA7PLL 2
+#define APMIXED_MAINPLL 3
+#define APMIXED_UNIVPLL 4
+#define APMIXED_MMPLL 5
+#define APMIXED_MSDCPLL 6
+#define APMIXED_VENCPLL 7
+#define APMIXED_TVDPLL 8
+#define APMIXED_MPLL 9
+#define APMIXED_VCODECPLL 10
+#define APMIXED_APLL1 11
+#define APMIXED_APLL2 12
+#define APMIXED_LVDSPLL 13
+#define APMIXED_MSDCPLL2 14
+#define APMIXED_NR_CLK 15
+
+/* INFRA_SYS */
+
+#define INFRA_DBGCLK 1
+#define INFRA_SMI 2
+#define INFRA_AUDIO 3
+#define INFRA_GCE 4
+#define INFRA_L2C_SRAM 5
+#define INFRA_M4U 6
+#define INFRA_CPUM 7
+#define INFRA_KP 8
+#define INFRA_CEC 9
+#define INFRA_PMICSPI 10
+#define INFRA_PMICWRAP 11
+#define INFRA_NR_CLK 12
+
+/* PERI_SYS */
+
+#define PERI_NFI 1
+#define PERI_THERM 2
+#define PERI_PWM1 3
+#define PERI_PWM2 4
+#define PERI_PWM3 5
+#define PERI_PWM4 6
+#define PERI_PWM5 7
+#define PERI_PWM6 8
+#define PERI_PWM7 9
+#define PERI_PWM 10
+#define PERI_USB0 11
+#define PERI_USB1 12
+#define PERI_AP_DMA 13
+#define PERI_MSDC30_0 14
+#define PERI_MSDC30_1 15
+#define PERI_MSDC30_2 16
+#define PERI_MSDC30_3 17
+#define PERI_NLI_ARB 18
+#define PERI_IRDA 19
+#define PERI_UART0 20
+#define PERI_UART1 21
+#define PERI_UART2 22
+#define PERI_UART3 23
+#define PERI_I2C0 24
+#define PERI_I2C1 25
+#define PERI_I2C2 26
+#define PERI_I2C3 27
+#define PERI_I2C4 28
+#define PERI_AUXADC 29
+#define PERI_SPI0 30
+#define PERI_I2C5 31
+#define PERI_NFIECC 32
+#define PERI_SPI 33
+#define PERI_IRRX 34
+#define PERI_I2C6 35
+#define PERI_NR_CLK 36
+
+#endif /* _DT_BINDINGS_CLK_MT8173_H */
--
2.1.4

2015-02-09 10:47:58

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 06/13] clk: mediatek: Add basic clocks for Mediatek MT8173.

From: James Liao <[email protected]>

This patch adds basic clocks for MT8173, including TOPCKGEN, PLLs,
INFRA and PERI clocks.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8173-pll.c | 807 +++++++++++++++++++++++++
drivers/clk/mediatek/clk-mt8173-pll.h | 14 +
drivers/clk/mediatek/clk-mt8173.c | 1035 +++++++++++++++++++++++++++++++++
4 files changed, 1857 insertions(+)
create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.c
create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.h
create mode 100644 drivers/clk/mediatek/clk-mt8173.c

diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index afb52e5..e030416 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,3 +1,4 @@
obj-y += clk-mtk.o clk-pll.o clk-gate.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
obj-y += clk-mt8135.o clk-mt8135-pll.o
+obj-y += clk-mt8173.o clk-mt8173-pll.o
diff --git a/drivers/clk/mediatek/clk-mt8173-pll.c b/drivers/clk/mediatek/clk-mt8173-pll.c
new file mode 100644
index 0000000..9f6f821
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-pll.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-mt8173-pll.h"
+
+#define PLL_BASE_EN BIT(0)
+#define PLL_PWR_ON BIT(0)
+#define PLL_ISO_EN BIT(1)
+#define PLL_PCW_CHG BIT(31)
+#define RST_BAR_MASK BIT(24)
+#define AUDPLL_TUNER_EN BIT(31)
+
+static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 };
+
+static u32 mtk_calc_pll_vco_freq(
+ u32 fin,
+ u32 pcw,
+ u32 vcodivsel,
+ u32 prediv,
+ u32 pcwfbits)
+{
+ /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */
+ u64 vco = fin;
+ u8 c = 0;
+
+ vco = vco * pcw * vcodivsel;
+ do_div(vco, prediv);
+
+ if (vco & GENMASK(pcwfbits - 1, 0))
+ c = 1;
+
+ vco >>= pcwfbits;
+
+ if (c)
+ ++vco;
+
+ return (u32)vco;
+}
+
+static u32 mtk_freq_limit(u32 freq)
+{
+ static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
+ static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 62.5 MHz */
+
+ if (freq <= freq_min)
+ freq = freq_min + 16;
+ else if (freq > freq_max)
+ freq = freq_max;
+
+ return freq;
+}
+
+static int mtk_calc_pll_freq_cfg(
+ u32 *pcw,
+ u32 *postdiv_idx,
+ u32 freq,
+ u32 fin,
+ int pcwfbits)
+{
+ static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
+ static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */
+ static const u64 postdiv[] = { 1, 2, 4, 8, 16 };
+ u64 n_info;
+ u32 idx;
+
+ /* search suitable postdiv */
+ for (idx = *postdiv_idx;
+ idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min;
+ idx++)
+ ;
+
+ if (idx >= ARRAY_SIZE(postdiv))
+ return -EINVAL; /* freq is out of range (too low) */
+ else if (postdiv[idx] * freq > freq_max)
+ return -EINVAL; /* freq is out of range (too high) */
+
+ /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */
+ n_info = (postdiv[idx] * freq) << pcwfbits;
+ do_div(n_info, fin);
+
+ *postdiv_idx = idx;
+ *pcw = (u32)n_info;
+
+ return 0;
+}
+
+static int mtk_clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ return (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0;
+}
+
+static int mtk_clk_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+#define SDM_PLL_POSTDIV_H 6
+#define SDM_PLL_POSTDIV_L 4
+#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L)
+#define SDM_PLL_PCW_H 20
+#define SDM_PLL_PCW_L 0
+#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L)
+
+static unsigned long mtk_clk_sdm_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_sdm_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~SDM_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << SDM_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~SDM_PLL_PCW_MASK;
+ con1 |= pcw << SDM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int mtk_clk_sdm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_sdm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8173_sdm_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_sdm_pll_recalc_rate,
+ .round_rate = mtk_clk_pll_round_rate,
+ .set_rate = mtk_clk_sdm_pll_set_rate,
+};
+
+#define ARM_PLL_POSTDIV_H 26
+#define ARM_PLL_POSTDIV_L 24
+#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L)
+#define ARM_PLL_PCW_H 20
+#define ARM_PLL_PCW_L 0
+#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L)
+
+static unsigned long mtk_clk_arm_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_arm_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con1 &= ~ARM_PLL_POSTDIV_MASK;
+ con1 |= postdiv_idx << ARM_PLL_POSTDIV_L;
+
+ /* pcw */
+ con1 &= ~ARM_PLL_PCW_MASK;
+ con1 |= pcw << ARM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int mtk_clk_arm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8173_arm_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_arm_pll_recalc_rate,
+ .round_rate = mtk_clk_pll_round_rate,
+ .set_rate = mtk_clk_arm_pll_set_rate,
+};
+
+static long mtk_clk_mm_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ if (rate <= 702000000)
+ postdiv = 2;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int mtk_clk_mm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ if (rate <= 702000000)
+ postdiv_idx = 2;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8173_mm_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_pll_prepare,
+ .unprepare = mtk_clk_pll_unprepare,
+ .recalc_rate = mtk_clk_arm_pll_recalc_rate,
+ .round_rate = mtk_clk_mm_pll_round_rate,
+ .set_rate = mtk_clk_mm_pll_set_rate,
+};
+
+static int mtk_clk_univ_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_univ_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+#define UNIV_PLL_POSTDIV_H 6
+#define UNIV_PLL_POSTDIV_L 4
+#define UNIV_PLL_POSTDIV_MASK GENMASK(UNIV_PLL_POSTDIV_H, UNIV_PLL_POSTDIV_L)
+#define UNIV_PLL_FBKDIV_H 20
+#define UNIV_PLL_FBKDIV_L 14
+#define UNIV_PLL_FBKDIV_MASK GENMASK(UNIV_PLL_FBKDIV_H, UNIV_PLL_FBKDIV_L)
+
+static unsigned long mtk_clk_univ_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 fbkdiv = (con1 & UNIV_PLL_FBKDIV_MASK) >> UNIV_PLL_FBKDIV_L;
+ u32 posdiv = (con0 & UNIV_PLL_POSTDIV_MASK) >> UNIV_PLL_POSTDIV_L;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = parent_rate * fbkdiv;
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_univ_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con0 &= ~UNIV_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << UNIV_PLL_POSTDIV_L;
+
+ /* fkbdiv */
+ con1 &= ~UNIV_PLL_FBKDIV_MASK;
+ con1 |= pcw << UNIV_PLL_FBKDIV_L;
+
+ writel_relaxed(con0, con0_addr);
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_univ_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = *prate * pcw;
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int mtk_clk_univ_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_univ_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8173_univ_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_univ_pll_prepare,
+ .unprepare = mtk_clk_univ_pll_unprepare,
+ .recalc_rate = mtk_clk_univ_pll_recalc_rate,
+ .round_rate = mtk_clk_univ_pll_round_rate,
+ .set_rate = mtk_clk_univ_pll_set_rate,
+};
+
+static int mtk_clk_aud_pll_prepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con2_addr = pll->base_addr + 8;
+ u32 r;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(con0_addr) | pll->en_mask;
+ writel_relaxed(r, con0_addr);
+
+ r = readl_relaxed(con2_addr) | AUDPLL_TUNER_EN;
+ writel_relaxed(r, con2_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(con0_addr) | RST_BAR_MASK;
+ writel_relaxed(r, con0_addr);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void mtk_clk_aud_pll_unprepare(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con2_addr = pll->base_addr + 8;
+ u32 r;
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(con0_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, con0_addr);
+ }
+
+ r = readl_relaxed(con2_addr) & ~AUDPLL_TUNER_EN;
+ writel_relaxed(r, con2_addr);
+
+ r = readl_relaxed(con0_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, con0_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+#define AUD_PLL_POSTDIV_H 6
+#define AUD_PLL_POSTDIV_L 4
+#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L)
+#define AUD_PLL_PCW_H 30
+#define AUD_PLL_PCW_L 0
+#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L)
+
+static unsigned long mtk_clk_aud_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L;
+ u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L;
+ u32 pcwfbits = 24;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ return r;
+}
+
+static void mtk_clk_aud_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ void __iomem *con2_addr = pll->base_addr + 8;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~AUD_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << AUD_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~AUD_PLL_PCW_MASK;
+ con1 |= pcw << AUD_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+ writel_relaxed(con1 + 1, con2_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static long mtk_clk_aud_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = mtk_freq_limit(rate);
+ mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int mtk_clk_aud_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
+ parent_rate, pcwfbits);
+
+ if (r == 0)
+ mtk_clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt8173_aud_pll_ops = {
+ .is_enabled = mtk_clk_pll_is_enabled,
+ .prepare = mtk_clk_aud_pll_prepare,
+ .unprepare = mtk_clk_aud_pll_unprepare,
+ .recalc_rate = mtk_clk_aud_pll_recalc_rate,
+ .round_rate = mtk_clk_aud_pll_round_rate,
+ .set_rate = mtk_clk_aud_pll_set_rate,
+};
diff --git a/drivers/clk/mediatek/clk-mt8173-pll.h b/drivers/clk/mediatek/clk-mt8173-pll.h
new file mode 100644
index 0000000..663ab4b
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-pll.h
@@ -0,0 +1,14 @@
+#ifndef __DRV_CLK_MT8173_PLL_H
+#define __DRV_CLK_MT8173_PLL_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+extern const struct clk_ops mt8173_sdm_pll_ops;
+extern const struct clk_ops mt8173_arm_pll_ops;
+extern const struct clk_ops mt8173_mm_pll_ops;
+extern const struct clk_ops mt8173_univ_pll_ops;
+extern const struct clk_ops mt8173_aud_pll_ops;
+
+#endif /* __DRV_CLK_MT8173_PLL_H */
diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
new file mode 100644
index 0000000..d75e591
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <[email protected]>
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-gate.h"
+#include "clk-mt8173-pll.h"
+
+#include <dt-bindings/clock/mt8173-clk.h>
+
+/* ROOT */
+#define clk_null "clk_null"
+#define clk26m "clk26m"
+#define clk32k "clk32k"
+
+#define clkph_mck_o "clkph_mck_o"
+#define dpi_ck "dpi_ck"
+#define usb_syspll_125m "usb_syspll_125m"
+#define hdmitx_dig_cts "hdmitx_dig_cts"
+
+/* PLL */
+#define armca15pll "armca15pll"
+#define armca7pll "armca7pll"
+#define mainpll "mainpll"
+#define univpll "univpll"
+#define mmpll "mmpll"
+#define msdcpll "msdcpll"
+#define vencpll "vencpll"
+#define tvdpll "tvdpll"
+#define mpll "mpll"
+#define vcodecpll "vcodecpll"
+#define apll1 "apll1"
+#define apll2 "apll2"
+#define lvdspll "lvdspll"
+#define msdcpll2 "msdcpll2"
+
+#define armca7pll_754m "armca7pll_754m"
+#define armca7pll_502m "armca7pll_502m"
+#define apll1_180p633m apll1
+#define apll2_196p608m apll2
+#define mmpll_455m mmpll
+#define msdcpll_806m msdcpll
+#define main_h546m "main_h546m"
+#define main_h364m "main_h364m"
+#define main_h218p4m "main_h218p4m"
+#define main_h156m "main_h156m"
+#define tvdpll_445p5m "tvdpll_445p5m"
+#define tvdpll_594m "tvdpll_594m"
+#define univ_624m "univ_624m"
+#define univ_416m "univ_416m"
+#define univ_249p6m "univ_249p6m"
+#define univ_178p3m "univ_178p3m"
+#define univ_48m "univ_48m"
+#define vcodecpll_370p5 "vcodecpll_370p5"
+#define vcodecpll_494m vcodecpll
+#define vencpll_380m vencpll
+#define lvdspll_ck lvdspll
+
+/* DIV */
+#define clkrtc_ext "clkrtc_ext"
+#define clkrtc_int "clkrtc_int"
+#define fpc_ck "fpc_ck"
+#define hdmitxpll_d2 "hdmitxpll_d2"
+#define hdmitxpll_d3 "hdmitxpll_d3"
+#define armca7pll_d2 "armca7pll_d2"
+#define armca7pll_d3 "armca7pll_d3"
+#define apll1_ck "apll1_ck"
+#define apll2_ck "apll2_ck"
+#define dmpll_ck "dmpll_ck"
+#define dmpll_d2 "dmpll_d2"
+#define dmpll_d4 "dmpll_d4"
+#define dmpll_d8 "dmpll_d8"
+#define dmpll_d16 "dmpll_d16"
+#define lvdspll_d2 "lvdspll_d2"
+#define lvdspll_d4 "lvdspll_d4"
+#define lvdspll_d8 "lvdspll_d8"
+#define mmpll_ck "mmpll_ck"
+#define mmpll_d2 "mmpll_d2"
+#define msdcpll_ck "msdcpll_ck"
+#define msdcpll_d2 "msdcpll_d2"
+#define msdcpll_d4 "msdcpll_d4"
+#define msdcpll2_ck "msdcpll2_ck"
+#define msdcpll2_d2 "msdcpll2_d2"
+#define msdcpll2_d4 "msdcpll2_d4"
+#define ssusb_phyd_125m_ck usb_syspll_125m
+#define syspll_d2 "syspll_d2"
+#define syspll1_d2 "syspll1_d2"
+#define syspll1_d4 "syspll1_d4"
+#define syspll1_d8 "syspll1_d8"
+#define syspll1_d16 "syspll1_d16"
+#define syspll_d3 "syspll_d3"
+#define syspll2_d2 "syspll2_d2"
+#define syspll2_d4 "syspll2_d4"
+#define syspll_d5 "syspll_d5"
+#define syspll3_d2 "syspll3_d2"
+#define syspll3_d4 "syspll3_d4"
+#define syspll_d7 "syspll_d7"
+#define syspll4_d2 "syspll4_d2"
+#define syspll4_d4 "syspll4_d4"
+#define tvdpll_445p5m_ck tvdpll_445p5m
+#define tvdpll_ck "tvdpll_ck"
+#define tvdpll_d2 "tvdpll_d2"
+#define tvdpll_d4 "tvdpll_d4"
+#define tvdpll_d8 "tvdpll_d8"
+#define tvdpll_d16 "tvdpll_d16"
+#define univpll_d2 "univpll_d2"
+#define univpll1_d2 "univpll1_d2"
+#define univpll1_d4 "univpll1_d4"
+#define univpll1_d8 "univpll1_d8"
+#define univpll_d3 "univpll_d3"
+#define univpll2_d2 "univpll2_d2"
+#define univpll2_d4 "univpll2_d4"
+#define univpll2_d8 "univpll2_d8"
+#define univpll_d5 "univpll_d5"
+#define univpll3_d2 "univpll3_d2"
+#define univpll3_d4 "univpll3_d4"
+#define univpll3_d8 "univpll3_d8"
+#define univpll_d7 "univpll_d7"
+#define univpll_d26 "univpll_d26"
+#define univpll_d52 "univpll_d52"
+#define vcodecpll_ck "vcodecpll_ck"
+#define vencpll_ck "vencpll_ck"
+#define vencpll_d2 "vencpll_d2"
+#define vencpll_d4 "vencpll_d4"
+
+/* TOP */
+#define axi_sel "axi_sel"
+#define mem_sel "mem_sel"
+#define ddrphycfg_sel "ddrphycfg_sel"
+#define mm_sel "mm_sel"
+#define pwm_sel "pwm_sel"
+#define vdec_sel "vdec_sel"
+#define venc_sel "venc_sel"
+#define mfg_sel "mfg_sel"
+#define camtg_sel "camtg_sel"
+#define uart_sel "uart_sel"
+#define spi_sel "spi_sel"
+#define usb20_sel "usb20_sel"
+#define usb30_sel "usb30_sel"
+#define msdc50_0_h_sel "msdc50_0_h_sel"
+#define msdc50_0_sel "msdc50_0_sel"
+#define msdc30_1_sel "msdc30_1_sel"
+#define msdc30_2_sel "msdc30_2_sel"
+#define msdc30_3_sel "msdc30_3_sel"
+#define audio_sel "audio_sel"
+#define aud_intbus_sel "aud_intbus_sel"
+#define pmicspi_sel "pmicspi_sel"
+#define scp_sel "scp_sel"
+#define atb_sel "atb_sel"
+#define venclt_sel "venclt_sel"
+#define dpi0_sel "dpi0_sel"
+#define irda_sel "irda_sel"
+#define cci400_sel "cci400_sel"
+#define aud_1_sel "aud_1_sel"
+#define aud_2_sel "aud_2_sel"
+#define mem_mfg_in_sel "mem_mfg_in_sel"
+#define axi_mfg_in_sel "axi_mfg_in_sel"
+#define scam_sel "scam_sel"
+#define spinfi_ifr_sel "spinfi_ifr_sel"
+#define hdmi_sel "hdmi_sel"
+#define dpilvds_sel "dpilvds_sel"
+#define msdc50_2_h_sel "msdc50_2_h_sel"
+#define hdcp_sel "hdcp_sel"
+#define hdcp_24m_sel "hdcp_24m_sel"
+#define rtc_sel "rtc_sel"
+
+#define axi_ck axi_sel
+#define mfg_ck mfg_sel
+
+/* INFRA */
+#define infra_pmicwrap "infra_pmicwrap"
+#define infra_pmicspi "infra_pmicspi"
+#define infra_cec "infra_cec"
+#define infra_kp "infra_kp"
+#define infra_cpum "infra_cpum"
+#define infra_m4u "infra_m4u"
+#define infra_l2c_sram "infra_l2c_sram"
+#define infra_gce "infra_gce"
+#define infra_audio "infra_audio"
+#define infra_smi "infra_smi"
+#define infra_dbgclk "infra_dbgclk"
+
+/* PERI0 */
+#define peri_nfiecc "peri_nfiecc"
+#define peri_i2c5 "peri_i2c5"
+#define peri_spi0 "peri_spi0"
+#define peri_auxadc "peri_auxadc"
+#define peri_i2c4 "peri_i2c4"
+#define peri_i2c3 "peri_i2c3"
+#define peri_i2c2 "peri_i2c2"
+#define peri_i2c1 "peri_i2c1"
+#define peri_i2c0 "peri_i2c0"
+#define peri_uart3 "peri_uart3"
+#define peri_uart2 "peri_uart2"
+#define peri_uart1 "peri_uart1"
+#define peri_uart0 "peri_uart0"
+#define peri_irda "peri_irda"
+#define peri_nli_arb "peri_nli_arb"
+#define peri_msdc30_3 "peri_msdc30_3"
+#define peri_msdc30_2 "peri_msdc30_2"
+#define peri_msdc30_1 "peri_msdc30_1"
+#define peri_msdc30_0 "peri_msdc30_0"
+#define peri_ap_dma "peri_ap_dma"
+#define peri_usb1 "peri_usb1"
+#define peri_usb0 "peri_usb0"
+#define peri_pwm "peri_pwm"
+#define peri_pwm7 "peri_pwm7"
+#define peri_pwm6 "peri_pwm6"
+#define peri_pwm5 "peri_pwm5"
+#define peri_pwm4 "peri_pwm4"
+#define peri_pwm3 "peri_pwm3"
+#define peri_pwm2 "peri_pwm2"
+#define peri_pwm1 "peri_pwm1"
+#define peri_therm "peri_therm"
+#define peri_nfi "peri_nfi"
+
+/* PERI1 */
+#define peri_i2c6 "peri_i2c6"
+#define peri_irrx "peri_irrx"
+#define peri_spi "peri_spi"
+
+static DEFINE_SPINLOCK(lock);
+
+static struct mtk_fixed_factor root_clk_alias[] __initdata = {
+ FACTOR(TOP_CLKPH_MCK_O, clkph_mck_o, clk_null, 1, 1),
+ FACTOR(TOP_DPI_CK, dpi_ck, clk_null, 1, 1),
+ FACTOR(TOP_USB_SYSPLL_125M, usb_syspll_125m, clk_null, 1, 1),
+ FACTOR(TOP_HDMITX_DIG_CTS, hdmitx_dig_cts, clk_null, 1, 1),
+};
+
+static struct mtk_fixed_factor top_divs[] __initdata = {
+ FACTOR(TOP_ARMCA7PLL_754M, armca7pll_754m, armca7pll, 1, 2),
+ FACTOR(TOP_ARMCA7PLL_502M, armca7pll_502m, armca7pll, 1, 3),
+
+ FACTOR(TOP_MAIN_H546M, main_h546m, mainpll, 1, 2),
+ FACTOR(TOP_MAIN_H364M, main_h364m, mainpll, 1, 3),
+ FACTOR(TOP_MAIN_H218P4M, main_h218p4m, mainpll, 1, 5),
+ FACTOR(TOP_MAIN_H156M, main_h156m, mainpll, 1, 7),
+
+ FACTOR(TOP_TVDPLL_445P5M, tvdpll_445p5m, tvdpll, 1, 4),
+ FACTOR(TOP_TVDPLL_594M, tvdpll_594m, tvdpll, 1, 3),
+
+ FACTOR(TOP_UNIV_624M, univ_624m, univpll, 1, 2),
+ FACTOR(TOP_UNIV_416M, univ_416m, univpll, 1, 3),
+ FACTOR(TOP_UNIV_249P6M, univ_249p6m, univpll, 1, 5),
+ FACTOR(TOP_UNIV_178P3M, univ_178p3m, univpll, 1, 7),
+ FACTOR(TOP_UNIV_48M, univ_48m, univpll, 1, 26),
+
+ FACTOR(TOP_CLKRTC_EXT, clkrtc_ext, clk32k, 1, 1),
+ FACTOR(TOP_CLKRTC_INT, clkrtc_int, clk26m, 1, 793),
+ FACTOR(TOP_FPC_CK, fpc_ck, clk26m, 1, 1),
+
+ FACTOR(TOP_HDMITXPLL_D2, hdmitxpll_d2, hdmitx_dig_cts, 1, 2),
+ FACTOR(TOP_HDMITXPLL_D3, hdmitxpll_d3, hdmitx_dig_cts, 1, 3),
+
+ FACTOR(TOP_ARMCA7PLL_D2, armca7pll_d2, armca7pll_754m, 1, 1),
+ FACTOR(TOP_ARMCA7PLL_D3, armca7pll_d3, armca7pll_502m, 1, 1),
+
+ FACTOR(TOP_APLL1_CK, apll1_ck, apll1_180p633m, 1, 1),
+ FACTOR(TOP_APLL2_CK, apll2_ck, apll2_196p608m, 1, 1),
+
+ FACTOR(TOP_DMPLL_CK, dmpll_ck, clkph_mck_o, 1, 1),
+ FACTOR(TOP_DMPLL_D2, dmpll_d2, clkph_mck_o, 1, 2),
+ FACTOR(TOP_DMPLL_D4, dmpll_d4, clkph_mck_o, 1, 4),
+ FACTOR(TOP_DMPLL_D8, dmpll_d8, clkph_mck_o, 1, 8),
+ FACTOR(TOP_DMPLL_D16, dmpll_d16, clkph_mck_o, 1, 16),
+
+ FACTOR(TOP_LVDSPLL_D2, lvdspll_d2, lvdspll, 1, 2),
+ FACTOR(TOP_LVDSPLL_D4, lvdspll_d4, lvdspll, 1, 4),
+ FACTOR(TOP_LVDSPLL_D8, lvdspll_d8, lvdspll, 1, 8),
+
+ FACTOR(TOP_MMPLL_CK, mmpll_ck, mmpll_455m, 1, 1),
+ FACTOR(TOP_MMPLL_D2, mmpll_d2, mmpll_455m, 1, 2),
+
+ FACTOR(TOP_MSDCPLL_CK, msdcpll_ck, msdcpll_806m, 1, 1),
+ FACTOR(TOP_MSDCPLL_D2, msdcpll_d2, msdcpll_806m, 1, 2),
+ FACTOR(TOP_MSDCPLL_D4, msdcpll_d4, msdcpll_806m, 1, 4),
+ FACTOR(TOP_MSDCPLL2_CK, msdcpll2_ck, msdcpll2, 1, 1),
+ FACTOR(TOP_MSDCPLL2_D2, msdcpll2_d2, msdcpll2, 1, 2),
+ FACTOR(TOP_MSDCPLL2_D4, msdcpll2_d4, msdcpll2, 1, 4),
+
+ FACTOR(TOP_SYSPLL_D2, syspll_d2, main_h546m, 1, 1),
+ FACTOR(TOP_SYSPLL1_D2, syspll1_d2, main_h546m, 1, 2),
+ FACTOR(TOP_SYSPLL1_D4, syspll1_d4, main_h546m, 1, 4),
+ FACTOR(TOP_SYSPLL1_D8, syspll1_d8, main_h546m, 1, 8),
+ FACTOR(TOP_SYSPLL1_D16, syspll1_d16, main_h546m, 1, 16),
+ FACTOR(TOP_SYSPLL_D3, syspll_d3, main_h364m, 1, 1),
+ FACTOR(TOP_SYSPLL2_D2, syspll2_d2, main_h364m, 1, 2),
+ FACTOR(TOP_SYSPLL2_D4, syspll2_d4, main_h364m, 1, 4),
+ FACTOR(TOP_SYSPLL_D5, syspll_d5, main_h218p4m, 1, 1),
+ FACTOR(TOP_SYSPLL3_D2, syspll3_d2, main_h218p4m, 1, 2),
+ FACTOR(TOP_SYSPLL3_D4, syspll3_d4, main_h218p4m, 1, 4),
+ FACTOR(TOP_SYSPLL_D7, syspll_d7, main_h156m, 1, 1),
+ FACTOR(TOP_SYSPLL4_D2, syspll4_d2, main_h156m, 1, 2),
+ FACTOR(TOP_SYSPLL4_D4, syspll4_d4, main_h156m, 1, 4),
+
+ FACTOR(TOP_TVDPLL_CK, tvdpll_ck, tvdpll_594m, 1, 1),
+ FACTOR(TOP_TVDPLL_D2, tvdpll_d2, tvdpll_594m, 1, 2),
+ FACTOR(TOP_TVDPLL_D4, tvdpll_d4, tvdpll_594m, 1, 4),
+ FACTOR(TOP_TVDPLL_D8, tvdpll_d8, tvdpll_594m, 1, 8),
+ FACTOR(TOP_TVDPLL_D16, tvdpll_d16, tvdpll_594m, 1, 16),
+
+ FACTOR(TOP_UNIVPLL_D2, univpll_d2, univ_624m, 1, 1),
+ FACTOR(TOP_UNIVPLL1_D2, univpll1_d2, univ_624m, 1, 2),
+ FACTOR(TOP_UNIVPLL1_D4, univpll1_d4, univ_624m, 1, 4),
+ FACTOR(TOP_UNIVPLL1_D8, univpll1_d8, univ_624m, 1, 8),
+ FACTOR(TOP_UNIVPLL_D3, univpll_d3, univ_416m, 1, 1),
+ FACTOR(TOP_UNIVPLL2_D2, univpll2_d2, univ_416m, 1, 2),
+ FACTOR(TOP_UNIVPLL2_D4, univpll2_d4, univ_416m, 1, 4),
+ FACTOR(TOP_UNIVPLL2_D8, univpll2_d8, univ_416m, 1, 8),
+ FACTOR(TOP_UNIVPLL_D5, univpll_d5, univ_249p6m, 1, 1),
+ FACTOR(TOP_UNIVPLL3_D2, univpll3_d2, univ_249p6m, 1, 2),
+ FACTOR(TOP_UNIVPLL3_D4, univpll3_d4, univ_249p6m, 1, 4),
+ FACTOR(TOP_UNIVPLL3_D8, univpll3_d8, univ_249p6m, 1, 8),
+ FACTOR(TOP_UNIVPLL_D7, univpll_d7, univ_178p3m, 1, 1),
+ FACTOR(TOP_UNIVPLL_D26, univpll_d26, univ_48m, 1, 1),
+ FACTOR(TOP_UNIVPLL_D52, univpll_d52, univ_48m, 1, 2),
+
+ FACTOR(TOP_VCODECPLL_CK, vcodecpll_ck, vcodecpll, 1, 3),
+ FACTOR(TOP_VCODECPLL_370P5, vcodecpll_370p5, vcodecpll, 1, 4),
+
+ FACTOR(TOP_VENCPLL_CK, vencpll_ck, vencpll_380m, 1, 1),
+ FACTOR(TOP_VENCPLL_D2, vencpll_d2, vencpll_380m, 1, 2),
+ FACTOR(TOP_VENCPLL_D4, vencpll_d4, vencpll_380m, 1, 4),
+};
+
+static const char *axi_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll_d5,
+ syspll1_d4,
+ univpll_d5,
+ univpll2_d2,
+ dmpll_d2,
+ dmpll_d4};
+
+static const char *mem_parents[] __initconst = {
+ clk26m,
+ dmpll_ck};
+
+static const char *ddrphycfg_parents[] __initconst = {
+ clk26m,
+ syspll1_d8};
+
+static const char *mm_parents[] __initconst = {
+ clk26m,
+ vencpll_d2,
+ main_h364m,
+ syspll1_d2,
+ syspll_d5,
+ syspll1_d4,
+ univpll1_d2,
+ univpll2_d2,
+ dmpll_d2};
+
+static const char *pwm_parents[] __initconst = {
+ clk26m,
+ univpll2_d4,
+ univpll3_d2,
+ univpll1_d4};
+
+static const char *vdec_parents[] __initconst = {
+ clk26m,
+ vcodecpll_ck,
+ tvdpll_445p5m_ck,
+ univpll_d3,
+ vencpll_d2,
+ syspll_d3,
+ univpll1_d2,
+ mmpll_d2,
+ dmpll_d2,
+ dmpll_d4};
+
+static const char *venc_parents[] __initconst = {
+ clk26m,
+ vcodecpll_ck,
+ tvdpll_445p5m_ck,
+ univpll_d3,
+ vencpll_d2,
+ syspll_d3,
+ univpll1_d2,
+ univpll2_d2,
+ dmpll_d2,
+ dmpll_d4};
+
+static const char *mfg_parents[] __initconst = {
+ clk26m,
+ mmpll_ck,
+ dmpll_ck,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ syspll_d3,
+ syspll1_d2,
+ syspll_d5,
+ univpll_d3,
+ univpll1_d2,
+ univpll_d5,
+ univpll2_d2};
+
+static const char *camtg_parents[] __initconst = {
+ clk26m,
+ univpll_d26,
+ univpll2_d2,
+ syspll3_d2,
+ syspll3_d4,
+ univpll1_d4};
+
+static const char *uart_parents[] __initconst = {
+ clk26m,
+ univpll2_d8};
+
+static const char *spi_parents[] __initconst = {
+ clk26m,
+ syspll3_d2,
+ syspll1_d4,
+ syspll4_d2,
+ univpll3_d2,
+ univpll2_d4,
+ univpll1_d8};
+
+static const char *usb20_parents[] __initconst = {
+ clk26m,
+ univpll1_d8,
+ univpll3_d4};
+
+static const char *usb30_parents[] __initconst = {
+ clk26m,
+ univpll3_d2,
+ ssusb_phyd_125m_ck,
+ univpll2_d4};
+
+static const char *msdc50_0_h_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll2_d2,
+ syspll4_d2,
+ univpll_d5,
+ univpll1_d4};
+
+static const char *msdc50_0_parents[] __initconst = {
+ clk26m,
+ msdcpll_ck,
+ msdcpll_d2,
+ univpll1_d4,
+ syspll2_d2,
+ syspll_d7,
+ msdcpll_d4,
+ vencpll_d4,
+ tvdpll_ck,
+ univpll_d2,
+ univpll1_d2,
+ mmpll_ck,
+ msdcpll2_ck,
+ msdcpll2_d2,
+ msdcpll2_d4};
+
+static const char *msdc30_1_parents[] __initconst = {
+ clk26m,
+ univpll2_d2,
+ msdcpll_d4,
+ univpll1_d4,
+ syspll2_d2,
+ syspll_d7,
+ univpll_d7,
+ vencpll_d4};
+
+static const char *msdc30_2_parents[] __initconst = {
+ clk26m,
+ univpll2_d2,
+ msdcpll_d4,
+ univpll1_d4,
+ syspll2_d2,
+ syspll_d7,
+ univpll_d7,
+ vencpll_d2};
+
+static const char *msdc30_3_parents[] __initconst = {
+ clk26m,
+ msdcpll2_ck,
+ msdcpll2_d2,
+ univpll2_d2,
+ msdcpll2_d4,
+ msdcpll_d4,
+ univpll1_d4,
+ syspll2_d2,
+ syspll_d7,
+ univpll_d7,
+ vencpll_d4,
+ msdcpll_ck,
+ msdcpll_d2,
+ msdcpll_d4};
+
+static const char *audio_parents[] __initconst = {
+ clk26m,
+ syspll3_d4,
+ syspll4_d4,
+ syspll1_d16};
+
+static const char *aud_intbus_parents[] __initconst = {
+ clk26m,
+ syspll1_d4,
+ syspll4_d2,
+ univpll3_d2,
+ univpll2_d8,
+ dmpll_d4,
+ dmpll_d8};
+
+static const char *pmicspi_parents[] __initconst = {
+ clk26m,
+ syspll1_d8,
+ syspll3_d4,
+ syspll1_d16,
+ univpll3_d4,
+ univpll_d26,
+ dmpll_d8,
+ dmpll_d16};
+
+static const char *scp_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ univpll_d5,
+ syspll_d5,
+ dmpll_d2,
+ dmpll_d4};
+
+static const char *atb_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ univpll_d5,
+ dmpll_d2};
+
+static const char *venc_lt_parents[] __initconst = {
+ clk26m,
+ univpll_d3,
+ vcodecpll_ck,
+ tvdpll_445p5m_ck,
+ vencpll_d2,
+ syspll_d3,
+ univpll1_d2,
+ univpll2_d2,
+ syspll1_d2,
+ univpll_d5,
+ vcodecpll_370p5,
+ dmpll_ck};
+
+static const char *dpi0_parents[] __initconst = {
+ clk26m,
+ tvdpll_d2,
+ tvdpll_d4,
+ clk26m,
+ clk26m,
+ tvdpll_d8,
+ tvdpll_d16};
+
+static const char *irda_parents[] __initconst = {
+ clk26m,
+ univpll2_d4,
+ syspll2_d4};
+
+static const char *cci400_parents[] __initconst = {
+ clk26m,
+ vencpll_ck,
+ armca7pll_754m,
+ armca7pll_502m,
+ univpll_d2,
+ syspll_d2,
+ msdcpll_ck,
+ dmpll_ck};
+
+static const char *aud_1_parents[] __initconst = {
+ clk26m,
+ apll1_ck,
+ univpll2_d4,
+ univpll2_d8};
+
+static const char *aud_2_parents[] __initconst = {
+ clk26m,
+ apll2_ck,
+ univpll2_d4,
+ univpll2_d8};
+
+static const char *mem_mfg_in_parents[] __initconst = {
+ clk26m,
+ mmpll_ck,
+ dmpll_ck,
+ clk26m};
+
+static const char *axi_mfg_in_parents[] __initconst = {
+ clk26m,
+ axi_ck,
+ dmpll_d2};
+
+static const char *scam_parents[] __initconst = {
+ clk26m,
+ syspll3_d2,
+ univpll2_d4,
+ dmpll_d4};
+
+static const char *spinfi_ifr_parents[] __initconst = {
+ clk26m,
+ univpll2_d8,
+ univpll3_d4,
+ syspll4_d2,
+ univpll2_d4,
+ univpll3_d2,
+ syspll1_d4,
+ univpll1_d4};
+
+static const char *hdmi_parents[] __initconst = {
+ clk26m,
+ hdmitx_dig_cts,
+ hdmitxpll_d2,
+ hdmitxpll_d3};
+
+static const char *dpilvds_parents[] __initconst = {
+ clk26m,
+ lvdspll_ck,
+ lvdspll_d2,
+ lvdspll_d4,
+ lvdspll_d8,
+ fpc_ck};
+
+static const char *msdc50_2_h_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll2_d2,
+ syspll4_d2,
+ univpll_d5,
+ univpll1_d4};
+
+static const char *hdcp_parents[] __initconst = {
+ clk26m,
+ syspll4_d2,
+ syspll3_d4,
+ univpll2_d4};
+
+static const char *hdcp_24m_parents[] __initconst = {
+ clk26m,
+ univpll_d26,
+ univpll_d52,
+ univpll2_d8};
+
+static const char *rtc_parents[] __initconst = {
+ clkrtc_int,
+ clkrtc_ext,
+ clk26m,
+ univpll3_d8};
+
+static struct mtk_mux top_muxes[] __initdata = {
+ /* CLK_CFG_0 */
+ MUX(TOP_AXI_SEL, axi_sel, axi_parents,
+ 0x0040, 0, 3, INVALID_MUX_GATE_BIT /* 7 */),
+ MUX(TOP_MEM_SEL, mem_sel, mem_parents,
+ 0x0040, 8, 1, INVALID_MUX_GATE_BIT /* 15 */),
+ MUX(TOP_DDRPHYCFG_SEL, ddrphycfg_sel, ddrphycfg_parents,
+ 0x0040, 16, 1, 23),
+ MUX(TOP_MM_SEL, mm_sel, mm_parents, 0x0040, 24, 4, 31),
+ /* CLK_CFG_1 */
+ MUX(TOP_PWM_SEL, pwm_sel, pwm_parents, 0x0050, 0, 2, 7),
+ MUX(TOP_VDEC_SEL, vdec_sel, vdec_parents, 0x0050, 8, 4, 15),
+ MUX(TOP_VENC_SEL, venc_sel, venc_parents, 0x0050, 16, 4, 23),
+ MUX(TOP_MFG_SEL, mfg_sel, mfg_parents, 0x0050, 24, 4, 31),
+ /* CLK_CFG_2 */
+ MUX(TOP_CAMTG_SEL, camtg_sel, camtg_parents, 0x0060, 0, 3, 7),
+ MUX(TOP_UART_SEL, uart_sel, uart_parents, 0x0060, 8, 1, 15),
+ MUX(TOP_SPI_SEL, spi_sel, spi_parents, 0x0060, 16, 3, 23),
+ MUX(TOP_USB20_SEL, usb20_sel, usb20_parents, 0x0060, 24, 2, 31),
+ /* CLK_CFG_3 */
+ MUX(TOP_USB30_SEL, usb30_sel, usb30_parents, 0x0070, 0, 2, 7),
+ MUX(TOP_MSDC50_0_H_SEL, msdc50_0_h_sel, msdc50_0_h_parents,
+ 0x0070, 8, 3, 15),
+ MUX(TOP_MSDC50_0_SEL, msdc50_0_sel, msdc50_0_parents,
+ 0x0070, 16, 4, 23),
+ MUX(TOP_MSDC30_1_SEL, msdc30_1_sel, msdc30_1_parents,
+ 0x0070, 24, 3, 31),
+ /* CLK_CFG_4 */
+ MUX(TOP_MSDC30_2_SEL, msdc30_2_sel, msdc30_2_parents, 0x0080, 0, 3, 7),
+ MUX(TOP_MSDC30_3_SEL, msdc30_3_sel, msdc30_3_parents, 0x0080, 8, 4, 15),
+ MUX(TOP_AUDIO_SEL, audio_sel, audio_parents, 0x0080, 16, 2, 23),
+ MUX(TOP_AUD_INTBUS_SEL, aud_intbus_sel, aud_intbus_parents,
+ 0x0080, 24, 3, 31),
+ /* CLK_CFG_5 */
+ MUX(TOP_PMICSPI_SEL, pmicspi_sel, pmicspi_parents,
+ 0x0090, 0, 3, 7 /* 7:5 */),
+ MUX(TOP_SCP_SEL, scp_sel, scp_parents, 0x0090, 8, 3, 15),
+ MUX(TOP_ATB_SEL, atb_sel, atb_parents, 0x0090, 16, 2, 23),
+ MUX(TOP_VENC_LT_SEL, venclt_sel, venc_lt_parents, 0x0090, 24, 4, 31),
+ /* CLK_CFG_6 */
+ MUX(TOP_DPI0_SEL, dpi0_sel, dpi0_parents, 0x00a0, 0, 3, 7),
+ MUX(TOP_IRDA_SEL, irda_sel, irda_parents, 0x00a0, 8, 2, 15),
+ MUX(TOP_CCI400_SEL, cci400_sel, cci400_parents, 0x00a0, 16, 3, 23),
+ MUX(TOP_AUD_1_SEL, aud_1_sel, aud_1_parents, 0x00a0, 24, 2, 31),
+ /* CLK_CFG_7 */
+ MUX(TOP_AUD_2_SEL, aud_2_sel, aud_2_parents, 0x00b0, 0, 2, 7),
+ MUX(TOP_MEM_MFG_IN_SEL, mem_mfg_in_sel, mem_mfg_in_parents,
+ 0x00b0, 8, 2, 15),
+ MUX(TOP_AXI_MFG_IN_SEL, axi_mfg_in_sel, axi_mfg_in_parents,
+ 0x00b0, 16, 2, 23),
+ MUX(TOP_SCAM_SEL, scam_sel, scam_parents, 0x00b0, 24, 2, 31),
+ /* CLK_CFG_12 */
+ MUX(TOP_SPINFI_IFR_SEL, spinfi_ifr_sel, spinfi_ifr_parents,
+ 0x00c0, 0, 3, 7),
+ MUX(TOP_HDMI_SEL, hdmi_sel, hdmi_parents, 0x00c0, 8, 2, 15),
+ MUX(TOP_DPILVDS_SEL, dpilvds_sel, dpilvds_parents, 0x00c0, 24, 3, 31),
+ /* CLK_CFG_13 */
+ MUX(TOP_MSDC50_2_H_SEL, msdc50_2_h_sel, msdc50_2_h_parents,
+ 0x00d0, 0, 3, 7),
+ MUX(TOP_HDCP_SEL, hdcp_sel, hdcp_parents, 0x00d0, 8, 2, 15),
+ MUX(TOP_HDCP_24M_SEL, hdcp_24m_sel, hdcp_24m_parents,
+ 0x00d0, 16, 2, 23),
+ MUX(TOP_RTC_SEL, rtc_sel, rtc_parents,
+ 0x00d0, 24, 2, INVALID_MUX_GATE_BIT /* 31 */),
+};
+
+static void __init mtk_init_clk_topckgen(void __iomem *top_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(top_muxes); i++) {
+ struct mtk_mux *mux = &top_muxes[i];
+
+ clk = mtk_clk_register_mux(mux->name,
+ mux->parent_names, mux->num_parents,
+ top_base + mux->reg, mux->shift, mux->width,
+ mux->gate, &lock);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ mux->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[mux->id] = clk;
+ }
+}
+
+static struct mtk_pll plls[] __initdata = {
+ PLL(APMIXED_ARMCA15PLL, armca15pll, clk26m, 0x0200, 0x020c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_arm_pll_ops),
+ PLL(APMIXED_ARMCA7PLL, armca7pll, clk26m, 0x0210, 0x021c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_arm_pll_ops),
+ PLL(APMIXED_MAINPLL, mainpll, clk26m, 0x0220, 0x022c, 0xf0000101,
+ HAVE_PLL_HP | HAVE_RST_BAR, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_UNIVPLL, univpll, clk26m, 0x0230, 0x023c, 0xfe000001,
+ HAVE_RST_BAR | HAVE_FIX_FRQ | PLL_AO, &mt8173_univ_pll_ops),
+ PLL(APMIXED_MMPLL, mmpll, clk26m, 0x0240, 0x024c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_mm_pll_ops),
+ PLL(APMIXED_MSDCPLL, msdcpll, clk26m, 0x0250, 0x025c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_VENCPLL, vencpll, clk26m, 0x0260, 0x026c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_TVDPLL, tvdpll, clk26m, 0x0270, 0x027c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_MPLL, mpll, clk26m, 0x0280, 0x028c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_VCODECPLL, vcodecpll, clk26m, 0x0290, 0x029c, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_APLL1, apll1, clk26m, 0x02a0, 0x02b0, 0x00000001,
+ HAVE_PLL_HP, &mt8173_aud_pll_ops),
+ PLL(APMIXED_APLL2, apll2, clk26m, 0x02b4, 0x02c4, 0x00000001,
+ HAVE_PLL_HP, &mt8173_aud_pll_ops),
+ PLL(APMIXED_LVDSPLL, lvdspll, clk26m, 0x02d0, 0x02dc, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+ PLL(APMIXED_MSDCPLL2, msdcpll2, clk26m, 0x02f0, 0x02fc, 0x00000001,
+ HAVE_PLL_HP, &mt8173_sdm_pll_ops),
+};
+
+static void __init mtk_init_clk_apmixedsys(void __iomem *apmixed_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(plls); i++) {
+ struct mtk_pll *pll = &plls[i];
+
+ clk = mtk_clk_register_pll(pll->name, pll->parent_name,
+ apmixed_base + pll->reg,
+ apmixed_base + pll->pwr_reg,
+ pll->en_mask, pll->flags, pll->ops, &lock);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ pll->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[pll->id] = clk;
+ }
+}
+
+static struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x0040,
+ .clr_ofs = 0x0044,
+ .sta_ofs = 0x0048,
+};
+
+static struct mtk_gate infra_clks[] __initdata = {
+ GATE(INFRA_DBGCLK, infra_dbgclk, axi_sel, infra_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_SMI, infra_smi, mm_sel, infra_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_AUDIO, infra_audio, aud_intbus_sel, infra_cg_regs,
+ 5, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_GCE, infra_gce, axi_sel, infra_cg_regs,
+ 6, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_L2C_SRAM, infra_l2c_sram, axi_sel, infra_cg_regs,
+ 7, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_M4U, infra_m4u, mem_sel, infra_cg_regs,
+ 8, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_CPUM, infra_cpum, clk_null, infra_cg_regs,
+ 15, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_KP, infra_kp, axi_sel, infra_cg_regs,
+ 16, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_CEC, infra_cec, clk26m, infra_cg_regs,
+ 18, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_PMICSPI, infra_pmicspi, pmicspi_sel, infra_cg_regs,
+ 22, &mtk_clk_gate_ops_setclr),
+ GATE(INFRA_PMICWRAP, infra_pmicwrap, axi_sel, infra_cg_regs,
+ 23, &mtk_clk_gate_ops_setclr),
+};
+
+static struct mtk_gate_regs peri0_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x0010,
+ .sta_ofs = 0x0018,
+};
+
+static struct mtk_gate_regs peri1_cg_regs = {
+ .set_ofs = 0x000c,
+ .clr_ofs = 0x0014,
+ .sta_ofs = 0x001c,
+};
+
+static struct mtk_gate peri_clks[] __initdata = {
+ /* PERI0 */
+ GATE(PERI_NFI, peri_nfi, axi_sel, peri0_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_THERM, peri_therm, axi_sel, peri0_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM1, peri_pwm1, axi_sel, peri0_cg_regs,
+ 2, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM2, peri_pwm2, axi_sel, peri0_cg_regs,
+ 3, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM3, peri_pwm3, axi_sel, peri0_cg_regs,
+ 4, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM4, peri_pwm4, axi_sel, peri0_cg_regs,
+ 5, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM5, peri_pwm5, axi_sel, peri0_cg_regs,
+ 6, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM6, peri_pwm6, axi_sel, peri0_cg_regs,
+ 7, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM7, peri_pwm7, axi_sel, peri0_cg_regs,
+ 8, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_PWM, peri_pwm, axi_sel, peri0_cg_regs,
+ 9, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB0, peri_usb0, usb20_sel, peri0_cg_regs,
+ 10, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_USB1, peri_usb1, usb20_sel, peri0_cg_regs,
+ 11, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_AP_DMA, peri_ap_dma, axi_sel, peri0_cg_regs,
+ 12, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_0, peri_msdc30_0, msdc50_0_sel, peri0_cg_regs,
+ 13, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_1, peri_msdc30_1, msdc30_1_sel, peri0_cg_regs,
+ 14, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_2, peri_msdc30_2, msdc30_2_sel, peri0_cg_regs,
+ 15, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_MSDC30_3, peri_msdc30_3, msdc30_3_sel, peri0_cg_regs,
+ 16, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_NLI_ARB, peri_nli_arb, axi_sel, peri0_cg_regs,
+ 17, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_IRDA, peri_irda, irda_sel, peri0_cg_regs,
+ 18, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART0, peri_uart0, uart_sel, peri0_cg_regs,
+ 19, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART1, peri_uart1, uart_sel, peri0_cg_regs,
+ 20, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART2, peri_uart2, uart_sel, peri0_cg_regs,
+ 21, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_UART3, peri_uart3, uart_sel, peri0_cg_regs,
+ 22, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C0, peri_i2c0, axi_sel, peri0_cg_regs,
+ 23, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C1, peri_i2c1, axi_sel, peri0_cg_regs,
+ 24, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C2, peri_i2c2, axi_sel, peri0_cg_regs,
+ 25, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C3, peri_i2c3, axi_sel, peri0_cg_regs,
+ 26, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C4, peri_i2c4, axi_sel, peri0_cg_regs,
+ 27, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_AUXADC, peri_auxadc, clk26m, peri0_cg_regs,
+ 28, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_SPI0, peri_spi0, spi_sel, peri0_cg_regs,
+ 29, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C5, peri_i2c5, axi_sel, peri0_cg_regs,
+ 30, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_NFIECC, peri_nfiecc, axi_sel, peri0_cg_regs,
+ 31, &mtk_clk_gate_ops_setclr),
+ /* PERI1 */
+ GATE(PERI_SPI, peri_spi, spi_sel, peri1_cg_regs,
+ 0, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_IRRX, peri_irrx, spi_sel, peri1_cg_regs,
+ 1, &mtk_clk_gate_ops_setclr),
+ GATE(PERI_I2C6, peri_i2c6, axi_sel, peri1_cg_regs,
+ 2, &mtk_clk_gate_ops_setclr),
+};
+
+static void __init mtk_topckgen_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(TOP_NR_CLK);
+
+ mtk_init_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
+ mtk_init_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
+ mtk_init_clk_topckgen(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+}
+CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init);
+
+static void __init mtk_apmixedsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(APMIXED_NR_CLK);
+
+ mtk_init_clk_apmixedsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+}
+CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys",
+ mtk_apmixedsys_init);
+
+static void __init mtk_infrasys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ struct regmap *regmap;
+ int r;
+
+ regmap = syscon_node_to_regmap(node);
+ if (IS_ERR(regmap)) {
+ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
+ PTR_ERR(regmap));
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(INFRA_NR_CLK);
+
+ mtk_init_clk_gates(regmap, infra_clks, ARRAY_SIZE(infra_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ mtk_register_reset_controller(node, 2, 0x30);
+}
+CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init);
+
+static void __init mtk_pericfg_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ struct regmap *regmap;
+ int r;
+
+ regmap = syscon_node_to_regmap(node);
+ if (IS_ERR(regmap)) {
+ pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
+ PTR_ERR(regmap));
+ return;
+ }
+
+ clk_data = mtk_alloc_clk_data(PERI_NR_CLK);
+
+ mtk_init_clk_gates(regmap, peri_clks, ARRAY_SIZE(peri_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ mtk_register_reset_controller(node, 2, 0);
+}
+CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init);
--
2.1.4

2015-02-09 10:47:36

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 07/13] dt: bindings: Add MediaTek MT8135/MT8173 reset controller defines

This adds the reset defines for the MT8135/MT8173 pericfg and infracfg
controllers. Needed for device trees to specify the reset numbers in
pericfg / infracfg reset consumers

Signed-off-by: Sascha Hauer <[email protected]>
---
.../dt-bindings/reset-controller/mt8135-resets.h | 64 ++++++++++++++++++++++
.../dt-bindings/reset-controller/mt8173-resets.h | 63 +++++++++++++++++++++
2 files changed, 127 insertions(+)
create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h
create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h

diff --git a/include/dt-bindings/reset-controller/mt8135-resets.h b/include/dt-bindings/reset-controller/mt8135-resets.h
new file mode 100644
index 0000000..1fb6295
--- /dev/null
+++ b/include/dt-bindings/reset-controller/mt8135-resets.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8135
+#define _DT_BINDINGS_RESET_CONTROLLER_MT8135
+
+/* INFRACFG resets */
+#define MT8135_INFRA_EMI_REG_RST 0
+#define MT8135_INFRA_DRAMC0_A0_RST 1
+#define MT8135_INFRA_CCIF0_RST 2
+#define MT8135_INFRA_APCIRQ_EINT_RST 3
+#define MT8135_INFRA_APXGPT_RST 4
+#define MT8135_INFRA_SCPSYS_RST 5
+#define MT8135_INFRA_CCIF1_RST 6
+#define MT8135_INFRA_PMIC_WRAP_RST 7
+#define MT8135_INFRA_KP_RST 8
+#define MT8135_INFRA_EMI_RST 32
+#define MT8135_INFRA_DRAMC0_RST 34
+#define MT8135_INFRA_SMI_RST 35
+#define MT8135_INFRA_M4U_RST 36
+
+/* PERICFG resets */
+#define MT8135_PERI_UART0_SW_RST 0
+#define MT8135_PERI_UART1_SW_RST 1
+#define MT8135_PERI_UART2_SW_RST 2
+#define MT8135_PERI_UART3_SW_RST 3
+#define MT8135_PERI_IRDA_SW_RST 4
+#define MT8135_PERI_PTP_SW_RST 5
+#define MT8135_PERI_AP_HIF_SW_RST 6
+#define MT8135_PERI_GPCU_SW_RST 7
+#define MT8135_PERI_MD_HIF_SW_RST 8
+#define MT8135_PERI_NLI_SW_RST 9
+#define MT8135_PERI_AUXADC_SW_RST 10
+#define MT8135_PERI_DMA_SW_RST 11
+#define MT8135_PERI_NFI_SW_RST 14
+#define MT8135_PERI_PWM_SW_RST 15
+#define MT8135_PERI_THERM_SW_RST 16
+#define MT8135_PERI_MSDC0_SW_RST 17
+#define MT8135_PERI_MSDC1_SW_RST 18
+#define MT8135_PERI_MSDC2_SW_RST 19
+#define MT8135_PERI_MSDC3_SW_RST 20
+#define MT8135_PERI_I2C0_SW_RST 22
+#define MT8135_PERI_I2C1_SW_RST 23
+#define MT8135_PERI_I2C2_SW_RST 24
+#define MT8135_PERI_I2C3_SW_RST 25
+#define MT8135_PERI_I2C4_SW_RST 26
+#define MT8135_PERI_I2C5_SW_RST 27
+#define MT8135_PERI_I2C6_SW_RST 28
+#define MT8135_PERI_USB_SW_RST 29
+#define MT8135_PERI_SPI1_SW_RST 33
+#define MT8135_PERI_PWRAP_BRIDGE_SW_RST 34
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8135 */
diff --git a/include/dt-bindings/reset-controller/mt8173-resets.h b/include/dt-bindings/reset-controller/mt8173-resets.h
new file mode 100644
index 0000000..9464b37
--- /dev/null
+++ b/include/dt-bindings/reset-controller/mt8173-resets.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8173
+#define _DT_BINDINGS_RESET_CONTROLLER_MT8173
+
+/* INFRACFG resets */
+#define MT8173_INFRA_EMI_REG_RST 0
+#define MT8173_INFRA_DRAMC0_A0_RST 1
+#define MT8173_INFRA_APCIRQ_EINT_RST 3
+#define MT8173_INFRA_APXGPT_RST 4
+#define MT8173_INFRA_SCPSYS_RST 5
+#define MT8173_INFRA_KP_RST 6
+#define MT8173_INFRA_PMIC_WRAP_RST 7
+#define MT8173_INFRA_MPIP_RST 8
+#define MT8173_INFRA_CEC_RST 9
+#define MT8173_INFRA_EMI_RST 32
+#define MT8173_INFRA_DRAMC0_RST 34
+#define MT8173_INFRA_APMIXEDSYS_RST 35
+#define MT8173_INFRA_MIPI_DSI_RST 36
+#define MT8173_INFRA_TRNG_RST 37
+#define MT8173_INFRA_SYSIRQ_RST 38
+#define MT8173_INFRA_MIPI_CSI_RST 39
+#define MT8173_INFRA_GCE_FAXI_RST 40
+#define MT8173_INFRA_MMIOMMURST 47
+
+
+/* PERICFG resets */
+#define MT8173_PERI_UART0_SW_RST 0
+#define MT8173_PERI_UART1_SW_RST 1
+#define MT8173_PERI_UART2_SW_RST 2
+#define MT8173_PERI_UART3_SW_RST 3
+#define MT8173_PERI_IRRX_SW_RST 4
+#define MT8173_PERI_PWM_SW_RST 8
+#define MT8173_PERI_AUXADC_SW_RST 10
+#define MT8173_PERI_DMA_SW_RST 11
+#define MT8173_PERI_I2C6_SW_RST 13
+#define MT8173_PERI_NFI_SW_RST 14
+#define MT8173_PERI_THERM_SW_RST 16
+#define MT8173_PERI_MSDC2_SW_RST 17
+#define MT8173_PERI_MSDC3_SW_RST 18
+#define MT8173_PERI_MSDC0_SW_RST 19
+#define MT8173_PERI_MSDC1_SW_RST 20
+#define MT8173_PERI_I2C0_SW_RST 22
+#define MT8173_PERI_I2C1_SW_RST 23
+#define MT8173_PERI_I2C2_SW_RST 24
+#define MT8173_PERI_I2C3_SW_RST 25
+#define MT8173_PERI_I2C4_SW_RST 26
+#define MT8173_PERI_HDMI_SW_RST 29
+#define MT8173_PERI_SPI0_SW_RST 33
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8173 */
--
2.1.4

2015-02-09 10:49:36

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 08/13] soc: mediatek: Add PMIC wrapper for MT8135 and MT6397 SoC

From: Flora Fu <[email protected]>

This adds support for the PMIC wrapper found on MediaTek MT8135 and
MT8173 SoCs.

On MediaTek MT8135, MT8173 and other SoCs the PMIC is connected via
SPI. The SPI master interface is not directly visible to the CPU, but
only through the PMIC wrapper inside the SoC. The communication between
the SoC and the PMIC can optionally be encrypted. Also a non standard
Dual IO SPI mode can be used to increase speed. The MT8135 also supports
a special feature named "IP Pairing". With IP Pairing the pins of some
SoC internal peripherals can be on the PMIC. The signals of these pins
are routed over the SPI bus using the pwrap bridge. Because of these
optional non SPI conform features the PMIC driver is not implemented as
a SPI bus master driver.

Signed-off-by: Flora Fu, MediaTek
Signed-off-by: Sascha Hauer <[email protected]>
---
.../devicetree/bindings/soc/mediatek/pwrap.txt | 56 ++
drivers/soc/mediatek/Kconfig | 11 +
drivers/soc/mediatek/Makefile | 1 +
drivers/soc/mediatek/mtk-pmic-wrap.c | 1027 ++++++++++++++++++++
4 files changed, 1095 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
create mode 100644 drivers/soc/mediatek/Kconfig
create mode 100644 drivers/soc/mediatek/Makefile
create mode 100644 drivers/soc/mediatek/mtk-pmic-wrap.c

diff --git a/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt b/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
new file mode 100644
index 0000000..66cc528
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt
@@ -0,0 +1,56 @@
+MediaTek PMIC Wrapper Driver
+
+This document describes the binding for the MediaTek PMIC wrapper.
+
+On MediaTek MT8135, MT8173 and other SoCs the PMIC is connected via
+SPI. The SPI master interface is not directly visible to the CPU, but
+only through the PMIC wrapper inside the SoC. The communication between
+the SoC and the PMIC can optionally be encrypted. Also a non standard
+Dual IO SPI mode can be used to increase speed.
+
+IP Pairing
+
+on MT8135 the pins of some SoC internal peripherals can be on the PMIC.
+The signals of these pins are routed over the SPI bus using the pwrap
+bridge. In the binding description below the properties needed for bridging
+are marked with "IP Pairing". These are optional on SoCs which do not support
+IP Pairing
+
+Required properties in pwrap device node.
+- compatible: "mediatek,mt8135-pwrap" or "mediatek,mt8173-pwrap"
+- interrupts: IRQ for pwrap in SOC
+- reg-names: Must include the following entries:
+ "pwrap": Main registers base
+ "pwrap-bridge": bridge base (IP Pairing)
+- reg: Must contain an entry for each entry in reg-names.
+- reset-names: Must include the following entries:
+ "pwrap"
+ "pwrap-bridge" (IP Pairing)
+- resets: Must contain an entry for each entry in reset-names.
+- clock-names: Must include the following entries:
+ "spi": SPI bus clock
+ "wrap": Main module clock
+- clocks: Must contain an entry for each entry in clock-names.
+
+Optional properities:
+- pmic: Mediatek PMIC MFD is the child device of pwrap
+ See the following for child node definitions:
+ Documentation/devicetree/bindings/mfd/mt6397.txt
+
+Example:
+ pwrap: pwrap@1000f000 {
+ compatible = "mediatek,mt8135-pwrap";
+ reg = <0 0x1000f000 0 0x1000>,
+ <0 0x11017000 0 0x1000>;
+ reg-names = "pwrap", "pwrap-bridge";
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&infracfg MT8135_INFRA_PMIC_WRAP_RST>,
+ <&pericfg MT8135_PERI_PWRAP_BRIDGE_SW_RST>;
+ reset-names = "pwrap", "pwrap-bridge";
+ clocks = <&clk26m>, <&clk26m>;
+ clock-names = "spi", "wrap";
+
+ pmic {
+ compatible = "mediatek,mt6397";
+ };
+ };
diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
new file mode 100644
index 0000000..b91665a
--- /dev/null
+++ b/drivers/soc/mediatek/Kconfig
@@ -0,0 +1,11 @@
+#
+# MediaTek SoC drivers
+#
+config MTK_PMIC_WRAP
+ tristate "MediaTek PMIC Wrapper Support"
+ depends on ARCH_MEDIATEK
+ select REGMAP
+ help
+ Say yes here to add support for MediaTek PMIC Wrapper found
+ on the MT8135 and MT8173 SoCs. The PMIC wrapper is a proprietary
+ hardware to connect the PMIC.
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
new file mode 100644
index 0000000..ecaf4de
--- /dev/null
+++ b/drivers/soc/mediatek/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
new file mode 100644
index 0000000..bd1f881
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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.
+ */
+#define DEBUG
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define PWRAP_MT8135_BRIDGE_IORD_ARB_EN 0x4
+#define PWRAP_MT8135_BRIDGE_WACS3_EN 0x10
+#define PWRAP_MT8135_BRIDGE_INIT_DONE3 0x14
+#define PWRAP_MT8135_BRIDGE_WACS4_EN 0x24
+#define PWRAP_MT8135_BRIDGE_INIT_DONE4 0x28
+#define PWRAP_MT8135_BRIDGE_INT_EN 0x38
+#define PWRAP_MT8135_BRIDGE_TIMER_EN 0x48
+#define PWRAP_MT8135_BRIDGE_WDT_UNIT 0x50
+#define PWRAP_MT8135_BRIDGE_WDT_SRC_EN 0x54
+
+/* macro for wrapper status */
+#define PWRAP_GET_WACS_RDATA(x) (((x) >> 0) & 0x0000ffff)
+#define PWRAP_GET_WACS_FSM(x) (((x) >> 16) & 0x00000007)
+#define PWRAP_GET_WACS_REQ(x) (((x) >> 19) & 0x00000001)
+#define PWRAP_STATE_SYNC_IDLE0 (1 << 20)
+#define PWRAP_STATE_INIT_DONE0 (1 << 21)
+
+/* macro for WACS FSM */
+#define PWRAP_WACS_FSM_IDLE 0x00
+#define PWRAP_WACS_FSM_REQ 0x02
+#define PWRAP_WACS_FSM_WFDLE 0x04
+#define PWRAP_WACS_FSM_WFVLDCLR 0x06
+#define PWRAP_WACS_INIT_DONE 0x01
+#define PWRAP_WACS_WACS_SYNC_IDLE 0x01
+#define PWRAP_WACS_SYNC_BUSY 0x00
+
+/* macro for device wrapper default value */
+#define PWRAP_DEW_READ_TEST_VAL 0x5aa5
+#define PWRAP_DEW_WRITE_TEST_VAL 0xa55a
+
+/* macro for manual command */
+#define PWRAP_MAN_CMD_SPI_WRITE (1 << 13)
+#define PWRAP_MAN_CMD_OP_CSH (0x0 << 8)
+#define PWRAP_MAN_CMD_OP_CSL (0x1 << 8)
+#define PWRAP_MAN_CMD_OP_CK (0x2 << 8)
+#define PWRAP_MAN_CMD_OP_OUTS (0x8 << 8)
+#define PWRAP_MAN_CMD_OP_OUTD (0x9 << 8)
+#define PWRAP_MAN_CMD_OP_OUTQ (0xa << 8)
+
+/* macro for slave device wrapper registers */
+#define PWRAP_DEW_BASE 0xbc00
+#define PWRAP_DEW_EVENT_OUT_EN (PWRAP_DEW_BASE + 0x0)
+#define PWRAP_DEW_DIO_EN (PWRAP_DEW_BASE + 0x2)
+#define PWRAP_DEW_EVENT_SRC_EN (PWRAP_DEW_BASE + 0x4)
+#define PWRAP_DEW_EVENT_SRC (PWRAP_DEW_BASE + 0x6)
+#define PWRAP_DEW_EVENT_FLAG (PWRAP_DEW_BASE + 0x8)
+#define PWRAP_DEW_READ_TEST (PWRAP_DEW_BASE + 0xa)
+#define PWRAP_DEW_WRITE_TEST (PWRAP_DEW_BASE + 0xc)
+#define PWRAP_DEW_CRC_EN (PWRAP_DEW_BASE + 0xe)
+#define PWRAP_DEW_CRC_VAL (PWRAP_DEW_BASE + 0x10)
+#define PWRAP_DEW_MON_GRP_SEL (PWRAP_DEW_BASE + 0x12)
+#define PWRAP_DEW_MON_FLAG_SEL (PWRAP_DEW_BASE + 0x14)
+#define PWRAP_DEW_EVENT_TEST (PWRAP_DEW_BASE + 0x16)
+#define PWRAP_DEW_CIPHER_KEY_SEL (PWRAP_DEW_BASE + 0x18)
+#define PWRAP_DEW_CIPHER_IV_SEL (PWRAP_DEW_BASE + 0x1a)
+#define PWRAP_DEW_CIPHER_LOAD (PWRAP_DEW_BASE + 0x1c)
+#define PWRAP_DEW_CIPHER_START (PWRAP_DEW_BASE + 0x1e)
+#define PWRAP_DEW_CIPHER_RDY (PWRAP_DEW_BASE + 0x20)
+#define PWRAP_DEW_CIPHER_MODE (PWRAP_DEW_BASE + 0x22)
+#define PWRAP_DEW_CIPHER_SWRST (PWRAP_DEW_BASE + 0x24)
+#define PWRAP_MT8173_DEW_CIPHER_IV0 (PWRAP_DEW_BASE + 0x26)
+#define PWRAP_MT8173_DEW_CIPHER_IV1 (PWRAP_DEW_BASE + 0x28)
+#define PWRAP_MT8173_DEW_CIPHER_IV2 (PWRAP_DEW_BASE + 0x2a)
+#define PWRAP_MT8173_DEW_CIPHER_IV3 (PWRAP_DEW_BASE + 0x2c)
+#define PWRAP_MT8173_DEW_CIPHER_IV4 (PWRAP_DEW_BASE + 0x2e)
+#define PWRAP_MT8173_DEW_CIPHER_IV5 (PWRAP_DEW_BASE + 0x30)
+
+/*
+ * FIXME: These shouldn't be here. These registers are on the PMIC,
+ * so they should be touched in the PMIC driver, not the driver
+ * granting access to it.
+ */
+#define MT6397_WRP_CKPDN 0x011a
+#define MT6397_WRP_RST_CON 0x0120
+#define MT6397_TOP_CKCON2 0x012a
+#define MT6397_TOP_CKCON3 0x01d4
+
+enum pwrap_regs {
+ PWRAP_MUX_SEL,
+ PWRAP_WRAP_EN,
+ PWRAP_DIO_EN,
+ PWRAP_SIDLY,
+ PWRAP_CSHEXT_WRITE,
+ PWRAP_CSHEXT_READ,
+ PWRAP_CSLEXT_START,
+ PWRAP_CSLEXT_END,
+ PWRAP_STAUPD_PRD,
+ PWRAP_STAUPD_GRPEN,
+ PWRAP_STAUPD_MAN_TRIG,
+ PWRAP_STAUPD_STA,
+ PWRAP_WRAP_STA,
+ PWRAP_HARB_INIT,
+ PWRAP_HARB_HPRIO,
+ PWRAP_HIPRIO_ARB_EN,
+ PWRAP_HARB_STA0,
+ PWRAP_HARB_STA1,
+ PWRAP_MAN_EN,
+ PWRAP_MAN_CMD,
+ PWRAP_MAN_RDATA,
+ PWRAP_MAN_VLDCLR,
+ PWRAP_WACS0_EN,
+ PWRAP_INIT_DONE0,
+ PWRAP_WACS0_CMD,
+ PWRAP_WACS0_RDATA,
+ PWRAP_WACS0_VLDCLR,
+ PWRAP_WACS1_EN,
+ PWRAP_INIT_DONE1,
+ PWRAP_WACS1_CMD,
+ PWRAP_WACS1_RDATA,
+ PWRAP_WACS1_VLDCLR,
+ PWRAP_WACS2_EN,
+ PWRAP_INIT_DONE2,
+ PWRAP_WACS2_CMD,
+ PWRAP_WACS2_RDATA,
+ PWRAP_WACS2_VLDCLR,
+ PWRAP_INT_EN,
+ PWRAP_INT_FLG_RAW,
+ PWRAP_INT_FLG,
+ PWRAP_INT_CLR,
+ PWRAP_SIG_ADR,
+ PWRAP_SIG_MODE,
+ PWRAP_SIG_VALUE,
+ PWRAP_SIG_ERRVAL,
+ PWRAP_CRC_EN,
+ PWRAP_TIMER_EN,
+ PWRAP_TIMER_STA,
+ PWRAP_WDT_UNIT,
+ PWRAP_WDT_SRC_EN,
+ PWRAP_WDT_FLG,
+ PWRAP_DEBUG_INT_SEL,
+ PWRAP_CIPHER_KEY_SEL,
+ PWRAP_CIPHER_IV_SEL,
+ PWRAP_CIPHER_RDY,
+ PWRAP_CIPHER_MODE,
+ PWRAP_CIPHER_SWRST,
+ PWRAP_DCM_EN,
+ PWRAP_DCM_DBC_PRD,
+
+ /* MT8135 only regs */
+ PWRAP_CSHEXT,
+ PWRAP_EVENT_IN_EN,
+ PWRAP_EVENT_DST_EN,
+ PWRAP_RRARB_INIT,
+ PWRAP_RRARB_EN,
+ PWRAP_RRARB_STA0,
+ PWRAP_RRARB_STA1,
+ PWRAP_EVENT_STA,
+ PWRAP_EVENT_STACLR,
+ PWRAP_CIPHER_LOAD,
+ PWRAP_CIPHER_START,
+
+ /* MT8173 only regs */
+ PWRAP_RDDMY,
+ PWRAP_SI_CK_CON,
+ PWRAP_DVFS_ADR0,
+ PWRAP_DVFS_WDATA0,
+ PWRAP_DVFS_ADR1,
+ PWRAP_DVFS_WDATA1,
+ PWRAP_DVFS_ADR2,
+ PWRAP_DVFS_WDATA2,
+ PWRAP_DVFS_ADR3,
+ PWRAP_DVFS_WDATA3,
+ PWRAP_DVFS_ADR4,
+ PWRAP_DVFS_WDATA4,
+ PWRAP_DVFS_ADR5,
+ PWRAP_DVFS_WDATA5,
+ PWRAP_DVFS_ADR6,
+ PWRAP_DVFS_WDATA6,
+ PWRAP_DVFS_ADR7,
+ PWRAP_DVFS_WDATA7,
+ PWRAP_SPMINF_STA,
+ PWRAP_CIPHER_EN,
+};
+
+static int mt8173_regs[] = {
+ [PWRAP_MUX_SEL] = 0x0,
+ [PWRAP_WRAP_EN] = 0x4,
+ [PWRAP_DIO_EN] = 0x8,
+ [PWRAP_SIDLY] = 0xc,
+ [PWRAP_RDDMY] = 0x10,
+ [PWRAP_SI_CK_CON] = 0x14,
+ [PWRAP_CSHEXT_WRITE] = 0x18,
+ [PWRAP_CSHEXT_READ] = 0x1c,
+ [PWRAP_CSLEXT_START] = 0x20,
+ [PWRAP_CSLEXT_END] = 0x24,
+ [PWRAP_STAUPD_PRD] = 0x28,
+ [PWRAP_STAUPD_GRPEN] = 0x2c,
+ [PWRAP_STAUPD_MAN_TRIG] = 0x40,
+ [PWRAP_STAUPD_STA] = 0x44,
+ [PWRAP_WRAP_STA] = 0x48,
+ [PWRAP_HARB_INIT] = 0x4c,
+ [PWRAP_HARB_HPRIO] = 0x50,
+ [PWRAP_HIPRIO_ARB_EN] = 0x54,
+ [PWRAP_HARB_STA0] = 0x58,
+ [PWRAP_HARB_STA1] = 0x5c,
+ [PWRAP_MAN_EN] = 0x60,
+ [PWRAP_MAN_CMD] = 0x64,
+ [PWRAP_MAN_RDATA] = 0x68,
+ [PWRAP_MAN_VLDCLR] = 0x6c,
+ [PWRAP_WACS0_EN] = 0x70,
+ [PWRAP_INIT_DONE0] = 0x74,
+ [PWRAP_WACS0_CMD] = 0x78,
+ [PWRAP_WACS0_RDATA] = 0x7c,
+ [PWRAP_WACS0_VLDCLR] = 0x80,
+ [PWRAP_WACS1_EN] = 0x84,
+ [PWRAP_INIT_DONE1] = 0x88,
+ [PWRAP_WACS1_CMD] = 0x8c,
+ [PWRAP_WACS1_RDATA] = 0x90,
+ [PWRAP_WACS1_VLDCLR] = 0x94,
+ [PWRAP_WACS2_EN] = 0x98,
+ [PWRAP_INIT_DONE2] = 0x9c,
+ [PWRAP_WACS2_CMD] = 0xa0,
+ [PWRAP_WACS2_RDATA] = 0xa4,
+ [PWRAP_WACS2_VLDCLR] = 0xa8,
+ [PWRAP_INT_EN] = 0xac,
+ [PWRAP_INT_FLG_RAW] = 0xb0,
+ [PWRAP_INT_FLG] = 0xb4,
+ [PWRAP_INT_CLR] = 0xb8,
+ [PWRAP_SIG_ADR] = 0xbc,
+ [PWRAP_SIG_MODE] = 0xc0,
+ [PWRAP_SIG_VALUE] = 0xc4,
+ [PWRAP_SIG_ERRVAL] = 0xc8,
+ [PWRAP_CRC_EN] = 0xcc,
+ [PWRAP_TIMER_EN] = 0xd0,
+ [PWRAP_TIMER_STA] = 0xd4,
+ [PWRAP_WDT_UNIT] = 0xd8,
+ [PWRAP_WDT_SRC_EN] = 0xdc,
+ [PWRAP_WDT_FLG] = 0xe0,
+ [PWRAP_DEBUG_INT_SEL] = 0xe4,
+ [PWRAP_DVFS_ADR0] = 0xe8,
+ [PWRAP_DVFS_WDATA0] = 0xec,
+ [PWRAP_DVFS_ADR1] = 0xf0,
+ [PWRAP_DVFS_WDATA1] = 0xf4,
+ [PWRAP_DVFS_ADR2] = 0xf8,
+ [PWRAP_DVFS_WDATA2] = 0xfc,
+ [PWRAP_DVFS_ADR3] = 0x100,
+ [PWRAP_DVFS_WDATA3] = 0x104,
+ [PWRAP_DVFS_ADR4] = 0x108,
+ [PWRAP_DVFS_WDATA4] = 0x10c,
+ [PWRAP_DVFS_ADR5] = 0x110,
+ [PWRAP_DVFS_WDATA5] = 0x114,
+ [PWRAP_DVFS_ADR6] = 0x118,
+ [PWRAP_DVFS_WDATA6] = 0x11c,
+ [PWRAP_DVFS_ADR7] = 0x120,
+ [PWRAP_DVFS_WDATA7] = 0x124,
+ [PWRAP_SPMINF_STA] = 0x128,
+ [PWRAP_CIPHER_KEY_SEL] = 0x12c,
+ [PWRAP_CIPHER_IV_SEL] = 0x130,
+ [PWRAP_CIPHER_EN] = 0x134,
+ [PWRAP_CIPHER_RDY] = 0x138,
+ [PWRAP_CIPHER_MODE] = 0x13c,
+ [PWRAP_CIPHER_SWRST] = 0x140,
+ [PWRAP_DCM_EN] = 0x144,
+ [PWRAP_DCM_DBC_PRD] = 0x148,
+};
+
+static int mt8135_regs[] = {
+ [PWRAP_MUX_SEL] = 0x0,
+ [PWRAP_WRAP_EN] = 0x4,
+ [PWRAP_DIO_EN] = 0x8,
+ [PWRAP_SIDLY] = 0xc,
+ [PWRAP_CSHEXT] = 0x10,
+ [PWRAP_CSHEXT_WRITE] = 0x14,
+ [PWRAP_CSHEXT_READ] = 0x18,
+ [PWRAP_CSLEXT_START] = 0x1c,
+ [PWRAP_CSLEXT_END] = 0x20,
+ [PWRAP_STAUPD_PRD] = 0x24,
+ [PWRAP_STAUPD_GRPEN] = 0x28,
+ [PWRAP_STAUPD_MAN_TRIG] = 0x2c,
+ [PWRAP_STAUPD_STA] = 0x30,
+ [PWRAP_EVENT_IN_EN] = 0x34,
+ [PWRAP_EVENT_DST_EN] = 0x38,
+ [PWRAP_WRAP_STA] = 0x3c,
+ [PWRAP_RRARB_INIT] = 0x40,
+ [PWRAP_RRARB_EN] = 0x44,
+ [PWRAP_RRARB_STA0] = 0x48,
+ [PWRAP_RRARB_STA1] = 0x4c,
+ [PWRAP_HARB_INIT] = 0x50,
+ [PWRAP_HARB_HPRIO] = 0x54,
+ [PWRAP_HIPRIO_ARB_EN] = 0x58,
+ [PWRAP_HARB_STA0] = 0x5c,
+ [PWRAP_HARB_STA1] = 0x60,
+ [PWRAP_MAN_EN] = 0x64,
+ [PWRAP_MAN_CMD] = 0x68,
+ [PWRAP_MAN_RDATA] = 0x6c,
+ [PWRAP_MAN_VLDCLR] = 0x70,
+ [PWRAP_WACS0_EN] = 0x74,
+ [PWRAP_INIT_DONE0] = 0x78,
+ [PWRAP_WACS0_CMD] = 0x7c,
+ [PWRAP_WACS0_RDATA] = 0x80,
+ [PWRAP_WACS0_VLDCLR] = 0x84,
+ [PWRAP_WACS1_EN] = 0x88,
+ [PWRAP_INIT_DONE1] = 0x8c,
+ [PWRAP_WACS1_CMD] = 0x90,
+ [PWRAP_WACS1_RDATA] = 0x94,
+ [PWRAP_WACS1_VLDCLR] = 0x98,
+ [PWRAP_WACS2_EN] = 0x9c,
+ [PWRAP_INIT_DONE2] = 0xa0,
+ [PWRAP_WACS2_CMD] = 0xa4,
+ [PWRAP_WACS2_RDATA] = 0xa8,
+ [PWRAP_WACS2_VLDCLR] = 0xac,
+ [PWRAP_INT_EN] = 0xb0,
+ [PWRAP_INT_FLG_RAW] = 0xb4,
+ [PWRAP_INT_FLG] = 0xb8,
+ [PWRAP_INT_CLR] = 0xbc,
+ [PWRAP_SIG_ADR] = 0xc0,
+ [PWRAP_SIG_MODE] = 0xc4,
+ [PWRAP_SIG_VALUE] = 0xc8,
+ [PWRAP_SIG_ERRVAL] = 0xcc,
+ [PWRAP_CRC_EN] = 0xd0,
+ [PWRAP_EVENT_STA] = 0xd4,
+ [PWRAP_EVENT_STACLR] = 0xd8,
+ [PWRAP_TIMER_EN] = 0xdc,
+ [PWRAP_TIMER_STA] = 0xe0,
+ [PWRAP_WDT_UNIT] = 0xe4,
+ [PWRAP_WDT_SRC_EN] = 0xe8,
+ [PWRAP_WDT_FLG] = 0xec,
+ [PWRAP_DEBUG_INT_SEL] = 0xf0,
+ [PWRAP_CIPHER_KEY_SEL] = 0x134,
+ [PWRAP_CIPHER_IV_SEL] = 0x138,
+ [PWRAP_CIPHER_LOAD] = 0x13c,
+ [PWRAP_CIPHER_START] = 0x140,
+ [PWRAP_CIPHER_RDY] = 0x144,
+ [PWRAP_CIPHER_MODE] = 0x148,
+ [PWRAP_CIPHER_SWRST] = 0x14c,
+ [PWRAP_DCM_EN] = 0x15c,
+ [PWRAP_DCM_DBC_PRD] = 0x160,
+};
+
+enum pwrap_type {
+ PWRAP_MT8135,
+ PWRAP_MT8173,
+};
+
+struct pmic_wrapper_type {
+ int *regs;
+ enum pwrap_type type;
+ u32 arb_en_all;
+};
+
+static struct pmic_wrapper_type pwrap_mt8135 = {
+ .regs = mt8135_regs,
+ .type = PWRAP_MT8135,
+ .arb_en_all = 0x1ff,
+};
+
+static struct pmic_wrapper_type pwrap_mt8173 = {
+ .regs = mt8173_regs,
+ .type = PWRAP_MT8173,
+ .arb_en_all = 0x3f,
+};
+
+struct pmic_wrapper {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *regmap;
+ int *regs;
+ enum pwrap_type type;
+ u32 arb_en_all;
+ struct clk *clk_spi;
+ struct clk *clk_wrap;
+ struct reset_control *rstc;
+
+ struct reset_control *rstc_bridge;
+ void __iomem *bridge_base;
+};
+
+static inline int pwrap_is_mt8135(struct pmic_wrapper *wrp)
+{
+ return wrp->type == PWRAP_MT8135;
+}
+
+static inline int pwrap_is_mt8173(struct pmic_wrapper *wrp)
+{
+ return wrp->type == PWRAP_MT8173;
+}
+
+static u32 pwrap_readl(struct pmic_wrapper *wrp, enum pwrap_regs reg)
+{
+ return readl(wrp->base + wrp->regs[reg]);
+}
+
+static void pwrap_writel(struct pmic_wrapper *wrp, u32 val, enum pwrap_regs reg)
+{
+ writel(val, wrp->base + wrp->regs[reg]);
+}
+
+static bool pwrap_is_fsm_idle(struct pmic_wrapper *wrp)
+{
+ u32 val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
+
+ return PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_IDLE;
+}
+
+static bool pwrap_is_fsm_vldclr(struct pmic_wrapper *wrp)
+{
+ u32 val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
+
+ return PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR;
+}
+
+static bool pwrap_is_sync_idle(struct pmic_wrapper *wrp)
+{
+ return pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_SYNC_IDLE0;
+}
+
+static bool pwrap_is_fsm_idle_and_sync_idle(struct pmic_wrapper *wrp)
+{
+ u32 val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
+
+ return (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_IDLE) &&
+ (val & PWRAP_STATE_SYNC_IDLE0);
+}
+
+static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
+ bool (*fp)(struct pmic_wrapper *))
+{
+ unsigned long timeout;
+
+ timeout = jiffies + usecs_to_jiffies(255);
+
+ do {
+ if (time_after(jiffies, timeout))
+ return fp(wrp) ? 0 : -ETIMEDOUT;
+ if (fp(wrp))
+ return 0;
+ } while (1);
+}
+
+static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
+{
+ int ret;
+ u32 val;
+
+ val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
+ if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
+ pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
+
+ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
+ if (ret)
+ return ret;
+
+ pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata,
+ PWRAP_WACS2_CMD);
+
+ return 0;
+}
+
+static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
+{
+ int ret;
+ u32 val;
+
+ val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
+ if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
+ pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
+
+ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
+ if (ret)
+ return ret;
+
+ pwrap_writel(wrp, (adr >> 1) << 16, PWRAP_WACS2_CMD);
+
+ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
+ if (ret)
+ return ret;
+
+ *rdata = PWRAP_GET_WACS_RDATA(pwrap_readl(wrp, PWRAP_WACS2_RDATA));
+
+ return 0;
+}
+
+static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata)
+{
+ return pwrap_read(context, adr, rdata);
+}
+
+static int pwrap_regmap_write(void *context, u32 adr, u32 wdata)
+{
+ return pwrap_write(context, adr, wdata);
+}
+
+static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
+{
+ int ret, i;
+
+ pwrap_writel(wrp, 0, PWRAP_HIPRIO_ARB_EN);
+ pwrap_writel(wrp, 0, PWRAP_WRAP_EN);
+ pwrap_writel(wrp, 1, PWRAP_MUX_SEL);
+ pwrap_writel(wrp, 1, PWRAP_MAN_EN);
+ pwrap_writel(wrp, 0, PWRAP_DIO_EN);
+
+ pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_CSL,
+ PWRAP_MAN_CMD);
+ pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_OUTS,
+ PWRAP_MAN_CMD);
+ pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_CSH,
+ PWRAP_MAN_CMD);
+
+ for (i = 0; i < 4; i++)
+ pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_OUTS,
+ PWRAP_MAN_CMD);
+
+ ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle);
+ if (ret) {
+ dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ pwrap_writel(wrp, 0, PWRAP_MAN_EN);
+ pwrap_writel(wrp, 0, PWRAP_MUX_SEL);
+
+ return 0;
+}
+
+/*
+ * pwrap_init_sidly - configure serial input delay
+ *
+ * This configures the serial input delay. We can configure 0, 2, 4 or 6ns
+ * delay. Do a read test with all possible values and chose the best delay.
+ */
+static int pwrap_init_sidly(struct pmic_wrapper *wrp)
+{
+ u32 rdata;
+ u32 i;
+ u32 pass = 0;
+ signed char dly[16] = {
+ -1, 0, 1, 0, 2, -1, 1, 1, 3, -1, -1, -1, 3, -1, 2, 1
+ };
+
+ for (i = 0; i < 4; i++) {
+ pwrap_writel(wrp, i, PWRAP_SIDLY);
+ pwrap_read(wrp, PWRAP_DEW_READ_TEST, &rdata);
+ printk("%s: 0x%04x\n", __func__, rdata);
+ if (rdata == PWRAP_DEW_READ_TEST_VAL) {
+ dev_dbg(wrp->dev, "[Read Test] pass, SIDLY=%x\n", i);
+ pass |= 1 << i;
+ }
+ }
+
+ if (dly[pass] < 0) {
+ dev_err(wrp->dev, "sidly pass range 0x%x not continuous\n",
+ pass);
+ return -EIO;
+ }
+
+ pwrap_writel(wrp, dly[pass], PWRAP_SIDLY);
+
+ return 0;
+}
+
+static int pwrap_init_reg_clock(struct pmic_wrapper *wrp)
+{
+ u32 wdata;
+ u32 rdata;
+ unsigned long rate_spi;
+ int ck_mhz;
+
+ rate_spi = clk_get_rate(wrp->clk_spi);
+
+ if (rate_spi > 26000000)
+ ck_mhz = 26;
+ else if (rate_spi > 18)
+ ck_mhz = 18;
+ else
+ ck_mhz = 0;
+
+ pwrap_read(wrp, MT6397_TOP_CKCON2, &rdata);
+ wdata = rdata & ~(0x3 << 10);
+
+ if (ck_mhz == 18)
+ wdata |= 0x1 << 10;
+
+ if (pwrap_write(wrp, MT6397_TOP_CKCON2, wdata)) {
+ dev_err(wrp->dev, "Enable PMIC TOP_CKCON2 fail\n");
+ return -EFAULT;
+ }
+
+ switch (ck_mhz) {
+ case 18:
+ if (pwrap_is_mt8135(wrp))
+ pwrap_writel(wrp, 0xc, PWRAP_CSHEXT);
+ pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE);
+ pwrap_writel(wrp, 0xc, PWRAP_CSHEXT_READ);
+ pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
+ pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
+ break;
+ case 26:
+ if (pwrap_is_mt8135(wrp))
+ pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
+ pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
+ pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
+ pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
+ pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
+ break;
+ case 0:
+ if (pwrap_is_mt8135(wrp))
+ pwrap_writel(wrp, 0xf, PWRAP_CSHEXT);
+ pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_WRITE);
+ pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_READ);
+ pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_START);
+ pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_END);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable PMIC side reg clock */
+ if (pwrap_write(wrp, MT6397_WRP_CKPDN, 0) ||
+ pwrap_write(wrp, MT6397_WRP_RST_CON, 0)) {
+ dev_err(wrp->dev, "Enable PMIC fail\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static bool pwrap_is_cipher_ready(struct pmic_wrapper *wrp)
+{
+ return pwrap_readl(wrp, PWRAP_CIPHER_RDY) & 1;
+}
+
+static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp)
+{
+ u32 rdata;
+ int ret;
+
+ ret = pwrap_read(wrp, PWRAP_DEW_CIPHER_RDY, &rdata);
+ if (ret)
+ return 0;
+
+ return rdata == 1;
+}
+
+static int pwrap_init_cipher(struct pmic_wrapper *wrp)
+{
+ int ret;
+ u32 rdata;
+
+ pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST);
+ pwrap_writel(wrp, 0x0, PWRAP_CIPHER_SWRST);
+ pwrap_writel(wrp, 0x1, PWRAP_CIPHER_KEY_SEL);
+ pwrap_writel(wrp, 0x2, PWRAP_CIPHER_IV_SEL);
+
+ if (pwrap_is_mt8135(wrp)) {
+ pwrap_writel(wrp, 1, PWRAP_CIPHER_LOAD);
+ pwrap_writel(wrp, 1, PWRAP_CIPHER_START);
+ } else {
+ pwrap_writel(wrp, 1, PWRAP_CIPHER_EN);
+ }
+
+ /* Config cipher mode @PMIC */
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_SWRST, 0x1);
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_SWRST, 0x0);
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_KEY_SEL, 0x1);
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_IV_SEL, 0x2);
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_LOAD, 0x1);
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_START, 0x1);
+
+ /* wait for cipher data ready@AP */
+ ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready);
+ if (ret) {
+ dev_err(wrp->dev, "cipher data ready@AP fail, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* wait for cipher data ready@PMIC */
+ ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready);
+ if (ret) {
+ dev_err(wrp->dev, "timeout waiting for cipher data ready@PMIC\n");
+ return ret;
+ }
+
+ /* wait for cipher mode idle */
+ pwrap_write(wrp, PWRAP_DEW_CIPHER_MODE, 0x1);
+ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
+ if (ret) {
+ dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret);
+ return ret;
+ }
+
+ pwrap_writel(wrp, 1, PWRAP_CIPHER_MODE);
+
+ /* Write Test */
+ if (pwrap_write(wrp, PWRAP_DEW_WRITE_TEST, PWRAP_DEW_WRITE_TEST_VAL) ||
+ pwrap_read(wrp, PWRAP_DEW_WRITE_TEST, &rdata) ||
+ (rdata != PWRAP_DEW_WRITE_TEST_VAL)) {
+ dev_err(wrp->dev, "rdata=0x%04X\n", rdata);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int pwrap_init(struct pmic_wrapper *wrp)
+{
+ int ret;
+ u32 rdata;
+
+ reset_control_reset(wrp->rstc);
+ if (wrp->rstc_bridge)
+ reset_control_reset(wrp->rstc_bridge);
+
+ if (pwrap_is_mt8173(wrp)) {
+ /* Enable DCM */
+ pwrap_writel(wrp, 3, PWRAP_DCM_EN);
+ pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
+ }
+
+ /* Reset SPI slave */
+ ret = pwrap_reset_spislave(wrp);
+ if (ret)
+ return ret;
+
+ pwrap_writel(wrp, 1, PWRAP_WRAP_EN);
+
+ pwrap_writel(wrp, wrp->arb_en_all, PWRAP_HIPRIO_ARB_EN);
+
+ pwrap_writel(wrp, 1, PWRAP_WACS2_EN);
+
+ ret = pwrap_init_reg_clock(wrp);
+ if (ret)
+ return ret;
+
+ /* Setup serial input delay */
+ ret = pwrap_init_sidly(wrp);
+ if (ret)
+ return ret;
+
+ if (pwrap_is_mt8173(wrp)) {
+ /*
+ * Enable PMIC
+ * (May not be necessary, depending on S/W partition)
+ * set dewrap clock bit and clear dewrap reset bit
+ */
+ if (pwrap_write(wrp, MT6397_WRP_CKPDN, 0) ||
+ pwrap_write(wrp, MT6397_WRP_RST_CON, 0)) {
+ dev_err(wrp->dev, "Enable PMIC fail\n");
+ return -EFAULT;
+ }
+ }
+
+ /* Enable dual IO mode */
+ pwrap_write(wrp, PWRAP_DEW_DIO_EN, 1);
+
+ /* Check IDLE & INIT_DONE in advance */
+ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
+ if (ret) {
+ dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ pwrap_writel(wrp, 1, PWRAP_DIO_EN);
+
+ /* Read Test */
+ pwrap_read(wrp, PWRAP_DEW_READ_TEST, &rdata);
+ if (rdata != PWRAP_DEW_READ_TEST_VAL) {
+ dev_err(wrp->dev, "Read test failed after switch to DIO mode: 0x%04x != 0x%04x\n",
+ PWRAP_DEW_READ_TEST_VAL, rdata);
+ return -EFAULT;
+ }
+
+ /* Enable encryption */
+ ret = pwrap_init_cipher(wrp);
+ if (ret)
+ return ret;
+
+ /* Signature checking - using CRC */
+ if (pwrap_write(wrp, PWRAP_DEW_CRC_EN, 0x1))
+ return -EFAULT;
+
+ pwrap_writel(wrp, 0x1, PWRAP_CRC_EN);
+ pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE);
+ pwrap_writel(wrp, PWRAP_DEW_CRC_VAL, PWRAP_SIG_ADR);
+ pwrap_writel(wrp, wrp->arb_en_all, PWRAP_HIPRIO_ARB_EN);
+
+ if (pwrap_is_mt8135(wrp))
+ pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN);
+
+ pwrap_writel(wrp, 0x1, PWRAP_WACS0_EN);
+ pwrap_writel(wrp, 0x1, PWRAP_WACS1_EN);
+ pwrap_writel(wrp, 0x1, PWRAP_WACS2_EN);
+ pwrap_writel(wrp, 0x5, PWRAP_STAUPD_PRD);
+ pwrap_writel(wrp, 0xff, PWRAP_STAUPD_GRPEN);
+ pwrap_writel(wrp, 0xf, PWRAP_WDT_UNIT);
+ pwrap_writel(wrp, 0xffffffff, PWRAP_WDT_SRC_EN);
+ pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
+ pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN);
+
+ /* switch event pin from usbdl mode to normal mode @ MT6397 */
+ if (pwrap_read(wrp, MT6397_TOP_CKCON3, &rdata) ||
+ pwrap_write(wrp, MT6397_TOP_CKCON3, (rdata & 0x0007))) {
+ dev_err(wrp->dev, "switch event pin fail\n");
+ return -EFAULT;
+ }
+
+ if (pwrap_is_mt8135(wrp)) {
+ /* enable pwrap events and pwrap bridge in AP side */
+ pwrap_writel(wrp, 0x1, PWRAP_EVENT_IN_EN);
+ pwrap_writel(wrp, 0xffff, PWRAP_EVENT_DST_EN);
+ writel(0x7f, wrp->bridge_base + PWRAP_MT8135_BRIDGE_IORD_ARB_EN);
+ writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS3_EN);
+ writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS4_EN);
+ writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_UNIT);
+ writel(0xffff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_SRC_EN);
+ writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_TIMER_EN);
+ writel(0x7ff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INT_EN);
+
+ /* enable PMIC event out and sources */
+ if (pwrap_write(wrp, PWRAP_DEW_EVENT_OUT_EN, 0x1) ||
+ pwrap_write(wrp, PWRAP_DEW_EVENT_SRC_EN, 0xffff)) {
+ dev_err(wrp->dev, "enable dewrap fail\n");
+ return -EFAULT;
+ }
+ } else {
+ /* PMIC_DEWRAP enables */
+ if (pwrap_write(wrp, PWRAP_DEW_EVENT_OUT_EN, 0x1) ||
+ pwrap_write(wrp, PWRAP_DEW_EVENT_SRC_EN, 0xffff)) {
+ dev_err(wrp->dev, "enable dewrap fail\n");
+ return -EFAULT;
+ }
+ }
+
+ /* Setup the init done registers */
+ pwrap_writel(wrp, 1, PWRAP_INIT_DONE2);
+ pwrap_writel(wrp, 1, PWRAP_INIT_DONE0);
+ pwrap_writel(wrp, 1, PWRAP_INIT_DONE1);
+
+ if (pwrap_is_mt8135(wrp)) {
+ writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE3);
+ writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE4);
+ }
+
+ return 0;
+}
+
+static irqreturn_t pwrap_interrupt(int irqno, void *dev_id)
+{
+ u32 rdata;
+ struct pmic_wrapper *wrp = dev_id;
+
+ rdata = pwrap_readl(wrp, PWRAP_INT_FLG);
+
+ dev_err(wrp->dev, "unexpected interrupt int=0x%x\n", rdata);
+
+ pwrap_writel(wrp, 0xffffffff, PWRAP_INT_CLR);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config pwrap_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+ .reg_read = pwrap_regmap_read,
+ .reg_write = pwrap_regmap_write,
+ .max_register = 0xffff,
+};
+
+static struct of_device_id of_pwrap_match_tbl[] = {
+ {
+ .compatible = "mediatek,mt8135-pwrap",
+ .data = &pwrap_mt8135,
+ }, {
+ .compatible = "mediatek,mt8173-pwrap",
+ .data = &pwrap_mt8173,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl);
+
+static int pwrap_probe(struct platform_device *pdev)
+{
+ int ret, irq;
+ struct pmic_wrapper *wrp;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(of_pwrap_match_tbl, &pdev->dev);
+ const struct pmic_wrapper_type *type;
+ struct resource *res;
+
+ wrp = devm_kzalloc(&pdev->dev, sizeof(*wrp), GFP_KERNEL);
+ if (!wrp)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, wrp);
+
+ type = of_id->data;
+ wrp->regs = type->regs;
+ wrp->type = type->type;
+ wrp->arb_en_all = type->arb_en_all;
+ wrp->dev = &pdev->dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
+ wrp->base = devm_ioremap_resource(wrp->dev, res);
+ if (IS_ERR(wrp->base))
+ return PTR_ERR(wrp->base);
+
+ wrp->rstc = devm_reset_control_get(wrp->dev, "pwrap");
+ if (IS_ERR(wrp->rstc)) {
+ ret = PTR_ERR(wrp->rstc);
+ dev_dbg(wrp->dev, "cannot get pwrap reset: %d\n", ret);
+ return ret;
+ }
+
+ if (pwrap_is_mt8135(wrp)) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pwrap-bridge");
+ wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
+ if (IS_ERR(wrp->bridge_base))
+ return PTR_ERR(wrp->bridge_base);
+
+ wrp->rstc_bridge = devm_reset_control_get(wrp->dev, "pwrap-bridge");
+ if (IS_ERR(wrp->rstc_bridge)) {
+ ret = PTR_ERR(wrp->rstc_bridge);
+ dev_dbg(wrp->dev, "cannot get pwrap-bridge reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ wrp->clk_spi = devm_clk_get(wrp->dev, "spi");
+ if (IS_ERR(wrp->clk_spi)) {
+ dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_spi));
+ return PTR_ERR(wrp->clk_spi);
+ }
+
+ wrp->clk_wrap = devm_clk_get(wrp->dev, "wrap");
+ if (IS_ERR(wrp->clk_wrap)) {
+ dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_wrap));
+ return PTR_ERR(wrp->clk_wrap);
+ }
+
+ ret = clk_prepare_enable(wrp->clk_spi);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(wrp->clk_wrap);
+ if (ret)
+ goto err_out1;
+
+ /* Enable internal dynamic clock */
+ pwrap_writel(wrp, 1, PWRAP_DCM_EN);
+ pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
+
+ /*
+ * The PMIC could already be initialized by the bootloader.
+ * Skip initialization here in this case.
+ */
+ if (!pwrap_readl(wrp, PWRAP_INIT_DONE2)) {
+ ret = pwrap_init(wrp);
+ if (ret) {
+ dev_dbg(wrp->dev, "init failed with %d\n", ret);
+ goto err_out2;
+ }
+ }
+
+ if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_INIT_DONE0)) {
+ dev_dbg(wrp->dev, "initialization isn't finished\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH,
+ "mt-pmic-pwrap", wrp);
+ if (ret)
+ goto err_out2;
+
+ wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, &pwrap_regmap_config);
+ if (IS_ERR(wrp->regmap))
+ return PTR_ERR(wrp->regmap);
+
+ ret = of_platform_populate(np, NULL, NULL, wrp->dev);
+ if (ret) {
+ dev_dbg(wrp->dev, "failed to create child devices at %s\n",
+ np->full_name);
+ goto err_out2;
+ }
+
+ return 0;
+
+err_out2:
+ clk_disable_unprepare(wrp->clk_wrap);
+err_out1:
+ clk_disable_unprepare(wrp->clk_spi);
+
+ return ret;
+}
+
+static struct platform_driver pwrap_drv = {
+ .driver = {
+ .name = "mt-pmic-pwrap",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_pwrap_match_tbl),
+ },
+ .probe = pwrap_probe,
+};
+
+module_platform_driver(pwrap_drv);
+
+MODULE_AUTHOR("Flora Fu, MediaTek");
+MODULE_DESCRIPTION("MediaTek MT8135 PMIC Wrapper Driver");
+MODULE_LICENSE("GPL");
--
2.1.4

2015-02-09 10:50:50

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 09/13] ARM: dts: mediatek: Enable clock support for Mediatek MT8135.

From: James Liao <[email protected]>

This patch adds MT8135 clock controllers into device tree.

Signed-off-by: James Liao <[email protected]>
Signed-off-by: Henry Chen <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/boot/dts/mt8135.dtsi | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi
index 7d56a98..7cb5e84 100644
--- a/arch/arm/boot/dts/mt8135.dtsi
+++ b/arch/arm/boot/dts/mt8135.dtsi
@@ -12,6 +12,7 @@
* GNU General Public License for more details.
*/

+#include <dt-bindings/clock/mt8135-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "skeleton64.dtsi"
@@ -86,6 +87,18 @@
clock-frequency = <32000>;
#clock-cells = <0>;
};
+
+ clk_null: clk_null {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <0>;
+ };
+
+ clk26m: clk26m {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <26000000>;
+ };
};

soc {
@@ -94,6 +107,26 @@
compatible = "simple-bus";
ranges;

+ topckgen: topckgen@10000000 {
+ compatible = "mediatek,mt8135-topckgen";
+ reg = <0 0x10000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ infracfg: infracfg@10001000 {
+ #reset-cells = <1>;
+ #clock-cells = <1>;
+ compatible = "mediatek,mt8135-infracfg", "syscon";
+ reg = <0 0x10001000 0 0x1000>;
+ };
+
+ pericfg: pericfg@10003000 {
+ #reset-cells = <1>;
+ #clock-cells = <1>;
+ compatible = "mediatek,mt8135-pericfg", "syscon";
+ reg = <0 0x10003000 0 0x1000>;
+ };
+
timer: timer@10008000 {
compatible = "mediatek,mt8135-timer",
"mediatek,mt6577-timer";
@@ -103,6 +136,12 @@
clock-names = "system-clk", "rtc-clk";
};

+ apmixedsys: apmixedsys@10209000 {
+ compatible = "mediatek,mt8135-apmixedsys";
+ reg = <0 0x10209000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
gic: interrupt-controller@10211000 {
compatible = "arm,cortex-a15-gic";
interrupt-controller;
--
2.1.4

2015-02-09 10:50:32

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 10/13] ARM: dts: mt8135: Add pmic wrapper nodes

This adds the pmic wrapper node to the MediaTek MT8135 dtsi file.

This unit is used to access the PMIC on MediaTek boards.

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/boot/dts/mt8135.dtsi | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi
index 7cb5e84..fb6d6db 100644
--- a/arch/arm/boot/dts/mt8135.dtsi
+++ b/arch/arm/boot/dts/mt8135.dtsi
@@ -15,6 +15,7 @@
#include <dt-bindings/clock/mt8135-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/reset-controller/mt8135-resets.h>
#include "skeleton64.dtsi"

/ {
@@ -127,6 +128,19 @@
reg = <0 0x10003000 0 0x1000>;
};

+ pwrap: pwrap@1000f000 {
+ compatible = "mediatek,mt8135-pwrap";
+ reg = <0 0x1000f000 0 0x1000>,
+ <0 0x11017000 0 0x1000>;
+ reg-names = "pwrap-base", "pwrap-bridge-base";
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&infracfg MT8135_INFRA_PMIC_WRAP_RST>,
+ <&pericfg MT8135_PERI_PWRAP_BRIDGE_SW_RST>;
+ reset-names = "pwrap", "pwrap-bridge";
+ clocks = <&clk26m>, <&clk26m>;
+ clock-names = "spi", "wrap";
+ };
+
timer: timer@10008000 {
compatible = "mediatek,mt8135-timer",
"mediatek,mt6577-timer";
--
2.1.4

2015-02-09 10:49:35

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 11/13] ARM: dts: mt8135-evbp1: Add PMIC support

The MT8135 eval board contains a MT6397 PMIC. This adds the
corresponding device node to the dts file.

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/boot/dts/mt8135-evbp1.dts | 193 +++++++++++++++++++++++++++++++++++++
1 file changed, 193 insertions(+)

diff --git a/arch/arm/boot/dts/mt8135-evbp1.dts b/arch/arm/boot/dts/mt8135-evbp1.dts
index a5adf97..3be2c8b 100644
--- a/arch/arm/boot/dts/mt8135-evbp1.dts
+++ b/arch/arm/boot/dts/mt8135-evbp1.dts
@@ -23,3 +23,196 @@
reg = <0 0x80000000 0 0x40000000>;
};
};
+
+&pwrap {
+ pmic: mt6397 {
+ compatible = "mediatek,mt6397";
+
+ mt6397regulator: mt6397regulator {
+ compatible = "mediatek,mt6397-regulator";
+
+ mt6397_vpca15_reg: buck_vpca15 {
+ regulator-compatible = "buck_vpca15";
+ regulator-name = "vpca15";
+ regulator-min-microvolt = < 850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vpca7_reg: buck_vpca7 {
+ regulator-compatible = "buck_vpca7";
+ regulator-name = "vpca7";
+ regulator-min-microvolt = < 850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vsramca15_reg: buck_vsramca15 {
+ regulator-compatible = "buck_vsramca15";
+ regulator-name = "vsramca15";
+ regulator-min-microvolt = < 850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vsramca7_reg: buck_vsramca7 {
+ regulator-compatible = "buck_vsramca7";
+ regulator-name = "vsramca7";
+ regulator-min-microvolt = < 850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vcore_reg: buck_vcore {
+ regulator-compatible = "buck_vcore";
+ regulator-name = "vcore";
+ regulator-min-microvolt = < 850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vgpu_reg: buck_vgpu {
+ regulator-compatible = "buck_vgpu";
+ regulator-name = "vgpu";
+ regulator-min-microvolt = < 700000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <12500>;
+ regulator-enable-ramp-delay = <115>;
+ };
+
+ mt6397_vdrm_reg: buck_vdrm {
+ regulator-compatible = "buck_vdrm";
+ regulator-name = "vdrm";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vio18_reg: buck_vio18 {
+ regulator-compatible = "buck_vio18";
+ regulator-name = "vio18";
+ regulator-min-microvolt = <1620000>;
+ regulator-max-microvolt = <1980000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vtcxo_reg: ldo_vtcxo {
+ regulator-compatible = "ldo_vtcxo";
+ regulator-name = "vtcxo";
+ regulator-always-on;
+ };
+
+ mt6397_va28_reg: ldo_va28 {
+ regulator-compatible = "ldo_va28";
+ regulator-name = "va28";
+ regulator-always-on;
+ };
+
+ mt6397_vcama_reg: ldo_vcama {
+ regulator-compatible = "ldo_vcama";
+ regulator-name = "vcama";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vio28_reg: ldo_vio28 {
+ regulator-compatible = "ldo_vio28";
+ regulator-name = "vio28";
+ regulator-always-on;
+ };
+
+ mt6397_vusb_reg: ldo_vusb {
+ regulator-compatible = "ldo_vusb";
+ regulator-name = "vusb";
+ };
+
+ mt6397_vmc_reg: ldo_vmc {
+ regulator-compatible = "ldo_vmc";
+ regulator-name = "vmc";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vmch_reg: ldo_vmch {
+ regulator-compatible = "ldo_vmch";
+ regulator-name = "vmch";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vemc_3v3_reg: ldo_vemc3v3 {
+ regulator-compatible = "ldo_vemc3v3";
+ regulator-name = "vemc_3v3";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vgp1_reg: ldo_vgp1 {
+ regulator-compatible = "ldo_vgp1";
+ regulator-name = "vcamd";
+ regulator-min-microvolt = <1220000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <240>;
+ };
+
+ mt6397_vgp2_reg: ldo_vgp2 {
+ regulator-compatible = "ldo_vgp2";
+ regulator-name = "vcamio";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vgp3_reg: ldo_vgp3 {
+ regulator-compatible = "ldo_vgp3";
+ regulator-name = "vcamaf";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vgp4_reg: ldo_vgp4 {
+ regulator-compatible = "ldo_vgp4";
+ regulator-name = "vgp4";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vgp5_reg: ldo_vgp5 {
+ regulator-compatible = "ldo_vgp5";
+ regulator-name = "vgp5";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vgp6_reg: ldo_vgp6 {
+ regulator-compatible = "ldo_vgp6";
+ regulator-name = "vgp6";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+
+ mt6397_vibr_reg: ldo_vibr {
+ regulator-compatible = "ldo_vibr";
+ regulator-name = "vibr";
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+ };
+ };
+};
--
2.1.4

2015-02-09 10:48:32

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 12/13] mfd: dt-bindings: Add bindings for the MediaTek MT6397 PMIC

Signed-off-by: Sascha Hauer <[email protected]>
---
Documentation/devicetree/bindings/mfd/mt6397.txt | 70 ++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/mt6397.txt

diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt
new file mode 100644
index 0000000..15043e6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/mt6397.txt
@@ -0,0 +1,70 @@
+MediaTek MT6397 Multifunction Device Driver
+
+MT6397 is a multifunction device with the following sub modules:
+- Regulator
+- RTC
+- Audio codec
+- GPIO
+- Clock
+
+It is interfaced to host controller using SPI interface by a proprietary hardware
+called PMIC wrapper or pwrap. MT6397 MFD is a child device of pwrap.
+See the following for pwarp node definitions:
+Documentation/devicetree/bindings/soc/pwrap.txt
+
+This document describes the binding for MFD device and its sub module.
+
+Required properties:
+compatible: "mediatek,mt6397"
+
+Optional subnodes:
+
+- rtc
+ Required properties:
+ - compatible: "mediatek,mt6397-rtc"
+- regulators
+ Required properties:
+ - compatible: "mediatek,mt6397-regulator"
+ see Documentation/devicetree/bindings/regulator/mt6397-regulator.txt
+- codec
+ Required properties:
+ - compatible: "mediatek,mt6397-codec"
+- clk
+ Required properties:
+ - compatible: "mediatek,mt6397-clk"
+
+Example:
+ pwrap: pwrap@1000f000 {
+ compatible = "mediatek,mt8135-pwrap";
+
+ ...
+
+ pmic {
+ compatible = "mediatek,mt6397";
+
+ codec: mt6397codec {
+ compatible = "mediatek,mt6397-codec";
+ };
+
+ regulators {
+ compatible = "mediatek,mt6397-regulator";
+
+ mt6397_vpca15_reg: buck_vpca15 {
+ regulator-compatible = "buck_vpca15";
+ regulator-name = "vpca15";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-ramp-delay = <12500>;
+ regulator-always-on;
+ };
+
+ mt6397_vgp4_reg: ldo_vgp4 {
+ regulator-compatible = "ldo_vgp4";
+ regulator-name = "vgp4";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-enable-ramp-delay = <218>;
+ };
+ };
+ };
+ };
--
2.1.4

2015-02-09 10:47:39

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 13/13] mfd: Add support for the MediaTek MT6397 PMIC

From: Flora Fu <[email protected]>

This adds support for the MediaTek MT6397 PMIC. This is a
multifunction device with the following sub modules:

- Regulator
- RTC
- Audio codec
- GPIO
- Clock

It is interfaced to the host controller using SPI interface by a proprietary
hardware called PMIC wrapper or pwrap. MT6397 MFD is a child device of the
pwrap.

Signed-off-by: Flora Fu, MediaTek
Signed-off-by: Sascha Hauer <[email protected]>
Cc: Samuel Ortiz <[email protected]>
Cc: Lee Jones <[email protected]>
---
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/mt6397-core.c | 223 +++++++++++++++++++++
include/linux/mfd/mt6397/core.h | 64 +++++++
include/linux/mfd/mt6397/registers.h | 362 +++++++++++++++++++++++++++++++++++
5 files changed, 660 insertions(+)
create mode 100644 drivers/mfd/mt6397-core.c
create mode 100644 include/linux/mfd/mt6397/core.h
create mode 100644 include/linux/mfd/mt6397/registers.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2e6b731..7782e95 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -489,6 +489,16 @@ config MFD_MAX8998
additional drivers must be enabled in order to use the functionality
of the device.

+config MFD_MT6397
+ tristate "MediaTek MT6397 PMIC Support"
+ select MFD_CORE
+ select IRQ_DOMAIN
+ help
+ Say yes here to add support for MediaTek MT6397 PMIC. This is
+ a Power Management IC. This driver provides common support for
+ accessing the device; additional drivers must be enabled in order
+ to use the functionality of the device.
+
config MFD_MENF21BMC
tristate "MEN 14F021P00 Board Management Controller Support"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 53467e2..329d4ed 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -179,3 +179,4 @@ obj-$(CONFIG_MFD_DLN2) += dln2.o

intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
new file mode 100644
index 0000000..b61c4eb
--- /dev/null
+++ b/drivers/mfd/mt6397-core.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6397/registers.h>
+
+static const struct mfd_cell mt6397_devs[] = {
+ {
+ .name = "mt6397-rtc",
+ .of_compatible = "mediatek,mt6397-rtc",
+ }, {
+ .name = "mt6397-regulator",
+ .of_compatible = "mediatek,mt6397-regulator",
+ }, {
+ .name = "mt6397-codec",
+ .of_compatible = "mediatek,mt6397-codec",
+ }, {
+ .name = "mt6397-clk",
+ .of_compatible = "mediatek,mt6397-clk",
+ },
+};
+
+static void mt6397_irq_lock(struct irq_data *data)
+{
+ struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
+
+ mutex_lock(&mt6397->irqlock);
+}
+
+static void mt6397_irq_sync_unlock(struct irq_data *data)
+{
+ struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
+
+ regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]);
+ regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]);
+
+ mutex_unlock(&mt6397->irqlock);
+}
+
+static void mt6397_irq_disable(struct irq_data *data)
+{
+ struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
+ int shift = data->hwirq & 0xf;
+ int reg = data->hwirq >> 4;
+
+ mt6397->irq_masks_cur[reg] &= ~BIT(shift);
+}
+
+static void mt6397_irq_enable(struct irq_data *data)
+{
+ struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
+ int shift = data->hwirq & 0xf;
+ int reg = data->hwirq >> 4;
+
+ mt6397->irq_masks_cur[reg] |= BIT(shift);
+}
+
+static struct irq_chip mt6397_irq_chip = {
+ .name = "mt6397-irq",
+ .irq_bus_lock = mt6397_irq_lock,
+ .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
+ .irq_enable = mt6397_irq_enable,
+ .irq_disable = mt6397_irq_disable,
+};
+
+static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
+ int irqbase)
+{
+ unsigned int status;
+ int i, irq, ret;
+
+ ret = regmap_read(mt6397->regmap, reg, &status);
+ if (ret) {
+ dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
+ return;
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (status & BIT(i)) {
+ irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
+ if (irq)
+ handle_nested_irq(irq);
+ }
+ }
+
+ regmap_write(mt6397->regmap, reg, status);
+}
+
+static irqreturn_t mt6397_irq_thread(int irq, void *data)
+{
+ struct mt6397_chip *mt6397 = data;
+
+ mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0);
+ mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16);
+
+ return IRQ_HANDLED;
+}
+
+static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct mt6397_chip *mt6397 = d->host_data;
+
+ irq_set_chip_data(irq, mt6397);
+ irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
+ irq_set_nested_thread(irq, 1);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops mt6397_irq_domain_ops = {
+ .map = mt6397_irq_domain_map,
+};
+
+static int mt6397_irq_init(struct mt6397_chip *mt6397)
+{
+ int ret;
+
+ mutex_init(&mt6397->irqlock);
+
+ /* Mask all interrupt sources */
+ regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0);
+ regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0);
+
+ mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
+ MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
+ if (!mt6397->irq_domain) {
+ dev_err(mt6397->dev, "could not create irq domain\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
+ mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
+ if (ret) {
+ dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
+ mt6397->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt6397_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mt6397_chip *mt6397;
+
+ mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL);
+ if (!mt6397)
+ return -ENOMEM;
+
+ mt6397->dev = &pdev->dev;
+ /*
+ * mt6397 MFD is child device of soc pmic wrapper.
+ * Regmap is set from its parent.
+ */
+ mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mt6397->regmap)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, mt6397);
+
+ mt6397->irq = platform_get_irq(pdev, 0);
+ if (mt6397->irq > 0) {
+ ret = mt6397_irq_init(mt6397);
+ if (ret)
+ return ret;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs,
+ ARRAY_SIZE(mt6397_devs), NULL, 0, NULL);
+ if (ret)
+ dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
+
+ return ret;
+}
+
+static int mt6397_remove(struct platform_device *pdev)
+{
+ mfd_remove_devices(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id mt6397_of_match[] = {
+ { .compatible = "mediatek,mt6397" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6397_of_match);
+
+static struct platform_driver mt6397_driver = {
+ .probe = mt6397_probe,
+ .remove = mt6397_remove,
+ .driver = {
+ .name = "mt6397",
+ .of_match_table = of_match_ptr(mt6397_of_match),
+ },
+};
+
+module_platform_driver(mt6397_driver);
+
+MODULE_AUTHOR("Flora Fu, MediaTek");
+MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mt6397");
diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h
new file mode 100644
index 0000000..cf5265b
--- /dev/null
+++ b/include/linux/mfd/mt6397/core.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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.
+ */
+
+#ifndef __MFD_MT6397_CORE_H__
+#define __MFD_MT6397_CORE_H__
+
+enum mt6397_irq_numbers {
+ MT6397_IRQ_SPKL_AB = 0,
+ MT6397_IRQ_SPKR_AB,
+ MT6397_IRQ_SPKL,
+ MT6397_IRQ_SPKR,
+ MT6397_IRQ_BAT_L,
+ MT6397_IRQ_BAT_H,
+ MT6397_IRQ_FG_BAT_L,
+ MT6397_IRQ_FG_BAT_H,
+ MT6397_IRQ_WATCHDOG,
+ MT6397_IRQ_PWRKEY,
+ MT6397_IRQ_THR_L,
+ MT6397_IRQ_THR_H,
+ MT6397_IRQ_VBATON_UNDET,
+ MT6397_IRQ_BVALID_DET,
+ MT6397_IRQ_CHRDET,
+ MT6397_IRQ_OV,
+ MT6397_IRQ_LDO,
+ MT6397_IRQ_HOMEKEY,
+ MT6397_IRQ_ACCDET,
+ MT6397_IRQ_AUDIO,
+ MT6397_IRQ_RTC,
+ MT6397_IRQ_PWRKEY_RSTB,
+ MT6397_IRQ_HDMI_SIFM,
+ MT6397_IRQ_HDMI_CEC,
+ MT6397_IRQ_VCA15,
+ MT6397_IRQ_VSRMCA15,
+ MT6397_IRQ_VCORE,
+ MT6397_IRQ_VGPU,
+ MT6397_IRQ_VIO18,
+ MT6397_IRQ_VPCA7,
+ MT6397_IRQ_VSRMCA7,
+ MT6397_IRQ_VDRM,
+ MT6397_IRQ_NR,
+};
+
+struct mt6397_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ int irq;
+ struct irq_domain *irq_domain;
+ struct mutex irqlock;
+ u16 irq_masks_cur[2];
+ u16 irq_masks_cache[2];
+};
+
+#endif /* __MFD_MT6397_CORE_H__ */
diff --git a/include/linux/mfd/mt6397/registers.h b/include/linux/mfd/mt6397/registers.h
new file mode 100644
index 0000000..f23a0a6
--- /dev/null
+++ b/include/linux/mfd/mt6397/registers.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu, MediaTek
+ *
+ * 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.
+ */
+
+#ifndef __MFD_MT6397_REGISTERS_H__
+#define __MFD_MT6397_REGISTERS_H__
+
+/* PMIC Registers */
+#define MT6397_CID 0x0100
+#define MT6397_TOP_CKPDN 0x0102
+#define MT6397_TOP_CKPDN_SET 0x0104
+#define MT6397_TOP_CKPDN_CLR 0x0106
+#define MT6397_TOP_CKPDN2 0x0108
+#define MT6397_TOP_CKPDN2_SET 0x010A
+#define MT6397_TOP_CKPDN2_CLR 0x010C
+#define MT6397_TOP_GPIO_CKPDN 0x010E
+#define MT6397_TOP_RST_CON 0x0114
+#define MT6397_WRP_CKPDN 0x011A
+#define MT6397_WRP_RST_CON 0x0120
+#define MT6397_TOP_RST_MISC 0x0126
+#define MT6397_TOP_CKCON1 0x0128
+#define MT6397_TOP_CKCON2 0x012A
+#define MT6397_TOP_CKTST1 0x012C
+#define MT6397_TOP_CKTST2 0x012E
+#define MT6397_OC_DEG_EN 0x0130
+#define MT6397_OC_CTL0 0x0132
+#define MT6397_OC_CTL1 0x0134
+#define MT6397_OC_CTL2 0x0136
+#define MT6397_INT_RSV 0x0138
+#define MT6397_TEST_CON0 0x013A
+#define MT6397_TEST_CON1 0x013C
+#define MT6397_STATUS0 0x013E
+#define MT6397_STATUS1 0x0140
+#define MT6397_PGSTATUS 0x0142
+#define MT6397_CHRSTATUS 0x0144
+#define MT6397_OCSTATUS0 0x0146
+#define MT6397_OCSTATUS1 0x0148
+#define MT6397_OCSTATUS2 0x014A
+#define MT6397_HDMI_PAD_IE 0x014C
+#define MT6397_TEST_OUT_L 0x014E
+#define MT6397_TEST_OUT_H 0x0150
+#define MT6397_TDSEL_CON 0x0152
+#define MT6397_RDSEL_CON 0x0154
+#define MT6397_GPIO_SMT_CON0 0x0156
+#define MT6397_GPIO_SMT_CON1 0x0158
+#define MT6397_GPIO_SMT_CON2 0x015A
+#define MT6397_GPIO_SMT_CON3 0x015C
+#define MT6397_DRV_CON0 0x015E
+#define MT6397_DRV_CON1 0x0160
+#define MT6397_DRV_CON2 0x0162
+#define MT6397_DRV_CON3 0x0164
+#define MT6397_DRV_CON4 0x0166
+#define MT6397_DRV_CON5 0x0168
+#define MT6397_DRV_CON6 0x016A
+#define MT6397_DRV_CON7 0x016C
+#define MT6397_DRV_CON8 0x016E
+#define MT6397_DRV_CON9 0x0170
+#define MT6397_DRV_CON10 0x0172
+#define MT6397_DRV_CON11 0x0174
+#define MT6397_DRV_CON12 0x0176
+#define MT6397_INT_CON0 0x0178
+#define MT6397_INT_CON1 0x017E
+#define MT6397_INT_STATUS0 0x0184
+#define MT6397_INT_STATUS1 0x0186
+#define MT6397_FQMTR_CON0 0x0188
+#define MT6397_FQMTR_CON1 0x018A
+#define MT6397_FQMTR_CON2 0x018C
+#define MT6397_EFUSE_DOUT_0_15 0x01C4
+#define MT6397_EFUSE_DOUT_16_31 0x01C6
+#define MT6397_EFUSE_DOUT_32_47 0x01C8
+#define MT6397_EFUSE_DOUT_48_63 0x01CA
+#define MT6397_SPI_CON 0x01CC
+#define MT6397_TOP_CKPDN3 0x01CE
+#define MT6397_TOP_CKCON3 0x01D4
+#define MT6397_EFUSE_DOUT_64_79 0x01D6
+#define MT6397_EFUSE_DOUT_80_95 0x01D8
+#define MT6397_EFUSE_DOUT_96_111 0x01DA
+#define MT6397_EFUSE_DOUT_112_127 0x01DC
+#define MT6397_EFUSE_DOUT_128_143 0x01DE
+#define MT6397_EFUSE_DOUT_144_159 0x01E0
+#define MT6397_EFUSE_DOUT_160_175 0x01E2
+#define MT6397_EFUSE_DOUT_176_191 0x01E4
+#define MT6397_EFUSE_DOUT_192_207 0x01E6
+#define MT6397_EFUSE_DOUT_208_223 0x01E8
+#define MT6397_EFUSE_DOUT_224_239 0x01EA
+#define MT6397_EFUSE_DOUT_240_255 0x01EC
+#define MT6397_EFUSE_DOUT_256_271 0x01EE
+#define MT6397_EFUSE_DOUT_272_287 0x01F0
+#define MT6397_EFUSE_DOUT_288_300 0x01F2
+#define MT6397_EFUSE_DOUT_304_319 0x01F4
+#define MT6397_BUCK_CON0 0x0200
+#define MT6397_BUCK_CON1 0x0202
+#define MT6397_BUCK_CON2 0x0204
+#define MT6397_BUCK_CON3 0x0206
+#define MT6397_BUCK_CON4 0x0208
+#define MT6397_BUCK_CON5 0x020A
+#define MT6397_BUCK_CON6 0x020C
+#define MT6397_BUCK_CON7 0x020E
+#define MT6397_BUCK_CON8 0x0210
+#define MT6397_BUCK_CON9 0x0212
+#define MT6397_VCA15_CON0 0x0214
+#define MT6397_VCA15_CON1 0x0216
+#define MT6397_VCA15_CON2 0x0218
+#define MT6397_VCA15_CON3 0x021A
+#define MT6397_VCA15_CON4 0x021C
+#define MT6397_VCA15_CON5 0x021E
+#define MT6397_VCA15_CON6 0x0220
+#define MT6397_VCA15_CON7 0x0222
+#define MT6397_VCA15_CON8 0x0224
+#define MT6397_VCA15_CON9 0x0226
+#define MT6397_VCA15_CON10 0x0228
+#define MT6397_VCA15_CON11 0x022A
+#define MT6397_VCA15_CON12 0x022C
+#define MT6397_VCA15_CON13 0x022E
+#define MT6397_VCA15_CON14 0x0230
+#define MT6397_VCA15_CON15 0x0232
+#define MT6397_VCA15_CON16 0x0234
+#define MT6397_VCA15_CON17 0x0236
+#define MT6397_VCA15_CON18 0x0238
+#define MT6397_VSRMCA15_CON0 0x023A
+#define MT6397_VSRMCA15_CON1 0x023C
+#define MT6397_VSRMCA15_CON2 0x023E
+#define MT6397_VSRMCA15_CON3 0x0240
+#define MT6397_VSRMCA15_CON4 0x0242
+#define MT6397_VSRMCA15_CON5 0x0244
+#define MT6397_VSRMCA15_CON6 0x0246
+#define MT6397_VSRMCA15_CON7 0x0248
+#define MT6397_VSRMCA15_CON8 0x024A
+#define MT6397_VSRMCA15_CON9 0x024C
+#define MT6397_VSRMCA15_CON10 0x024E
+#define MT6397_VSRMCA15_CON11 0x0250
+#define MT6397_VSRMCA15_CON12 0x0252
+#define MT6397_VSRMCA15_CON13 0x0254
+#define MT6397_VSRMCA15_CON14 0x0256
+#define MT6397_VSRMCA15_CON15 0x0258
+#define MT6397_VSRMCA15_CON16 0x025A
+#define MT6397_VSRMCA15_CON17 0x025C
+#define MT6397_VSRMCA15_CON18 0x025E
+#define MT6397_VSRMCA15_CON19 0x0260
+#define MT6397_VSRMCA15_CON20 0x0262
+#define MT6397_VSRMCA15_CON21 0x0264
+#define MT6397_VCORE_CON0 0x0266
+#define MT6397_VCORE_CON1 0x0268
+#define MT6397_VCORE_CON2 0x026A
+#define MT6397_VCORE_CON3 0x026C
+#define MT6397_VCORE_CON4 0x026E
+#define MT6397_VCORE_CON5 0x0270
+#define MT6397_VCORE_CON6 0x0272
+#define MT6397_VCORE_CON7 0x0274
+#define MT6397_VCORE_CON8 0x0276
+#define MT6397_VCORE_CON9 0x0278
+#define MT6397_VCORE_CON10 0x027A
+#define MT6397_VCORE_CON11 0x027C
+#define MT6397_VCORE_CON12 0x027E
+#define MT6397_VCORE_CON13 0x0280
+#define MT6397_VCORE_CON14 0x0282
+#define MT6397_VCORE_CON15 0x0284
+#define MT6397_VCORE_CON16 0x0286
+#define MT6397_VCORE_CON17 0x0288
+#define MT6397_VCORE_CON18 0x028A
+#define MT6397_VGPU_CON0 0x028C
+#define MT6397_VGPU_CON1 0x028E
+#define MT6397_VGPU_CON2 0x0290
+#define MT6397_VGPU_CON3 0x0292
+#define MT6397_VGPU_CON4 0x0294
+#define MT6397_VGPU_CON5 0x0296
+#define MT6397_VGPU_CON6 0x0298
+#define MT6397_VGPU_CON7 0x029A
+#define MT6397_VGPU_CON8 0x029C
+#define MT6397_VGPU_CON9 0x029E
+#define MT6397_VGPU_CON10 0x02A0
+#define MT6397_VGPU_CON11 0x02A2
+#define MT6397_VGPU_CON12 0x02A4
+#define MT6397_VGPU_CON13 0x02A6
+#define MT6397_VGPU_CON14 0x02A8
+#define MT6397_VGPU_CON15 0x02AA
+#define MT6397_VGPU_CON16 0x02AC
+#define MT6397_VGPU_CON17 0x02AE
+#define MT6397_VGPU_CON18 0x02B0
+#define MT6397_VIO18_CON0 0x0300
+#define MT6397_VIO18_CON1 0x0302
+#define MT6397_VIO18_CON2 0x0304
+#define MT6397_VIO18_CON3 0x0306
+#define MT6397_VIO18_CON4 0x0308
+#define MT6397_VIO18_CON5 0x030A
+#define MT6397_VIO18_CON6 0x030C
+#define MT6397_VIO18_CON7 0x030E
+#define MT6397_VIO18_CON8 0x0310
+#define MT6397_VIO18_CON9 0x0312
+#define MT6397_VIO18_CON10 0x0314
+#define MT6397_VIO18_CON11 0x0316
+#define MT6397_VIO18_CON12 0x0318
+#define MT6397_VIO18_CON13 0x031A
+#define MT6397_VIO18_CON14 0x031C
+#define MT6397_VIO18_CON15 0x031E
+#define MT6397_VIO18_CON16 0x0320
+#define MT6397_VIO18_CON17 0x0322
+#define MT6397_VIO18_CON18 0x0324
+#define MT6397_VPCA7_CON0 0x0326
+#define MT6397_VPCA7_CON1 0x0328
+#define MT6397_VPCA7_CON2 0x032A
+#define MT6397_VPCA7_CON3 0x032C
+#define MT6397_VPCA7_CON4 0x032E
+#define MT6397_VPCA7_CON5 0x0330
+#define MT6397_VPCA7_CON6 0x0332
+#define MT6397_VPCA7_CON7 0x0334
+#define MT6397_VPCA7_CON8 0x0336
+#define MT6397_VPCA7_CON9 0x0338
+#define MT6397_VPCA7_CON10 0x033A
+#define MT6397_VPCA7_CON11 0x033C
+#define MT6397_VPCA7_CON12 0x033E
+#define MT6397_VPCA7_CON13 0x0340
+#define MT6397_VPCA7_CON14 0x0342
+#define MT6397_VPCA7_CON15 0x0344
+#define MT6397_VPCA7_CON16 0x0346
+#define MT6397_VPCA7_CON17 0x0348
+#define MT6397_VPCA7_CON18 0x034A
+#define MT6397_VSRMCA7_CON0 0x034C
+#define MT6397_VSRMCA7_CON1 0x034E
+#define MT6397_VSRMCA7_CON2 0x0350
+#define MT6397_VSRMCA7_CON3 0x0352
+#define MT6397_VSRMCA7_CON4 0x0354
+#define MT6397_VSRMCA7_CON5 0x0356
+#define MT6397_VSRMCA7_CON6 0x0358
+#define MT6397_VSRMCA7_CON7 0x035A
+#define MT6397_VSRMCA7_CON8 0x035C
+#define MT6397_VSRMCA7_CON9 0x035E
+#define MT6397_VSRMCA7_CON10 0x0360
+#define MT6397_VSRMCA7_CON11 0x0362
+#define MT6397_VSRMCA7_CON12 0x0364
+#define MT6397_VSRMCA7_CON13 0x0366
+#define MT6397_VSRMCA7_CON14 0x0368
+#define MT6397_VSRMCA7_CON15 0x036A
+#define MT6397_VSRMCA7_CON16 0x036C
+#define MT6397_VSRMCA7_CON17 0x036E
+#define MT6397_VSRMCA7_CON18 0x0370
+#define MT6397_VSRMCA7_CON19 0x0372
+#define MT6397_VSRMCA7_CON20 0x0374
+#define MT6397_VSRMCA7_CON21 0x0376
+#define MT6397_VDRM_CON0 0x0378
+#define MT6397_VDRM_CON1 0x037A
+#define MT6397_VDRM_CON2 0x037C
+#define MT6397_VDRM_CON3 0x037E
+#define MT6397_VDRM_CON4 0x0380
+#define MT6397_VDRM_CON5 0x0382
+#define MT6397_VDRM_CON6 0x0384
+#define MT6397_VDRM_CON7 0x0386
+#define MT6397_VDRM_CON8 0x0388
+#define MT6397_VDRM_CON9 0x038A
+#define MT6397_VDRM_CON10 0x038C
+#define MT6397_VDRM_CON11 0x038E
+#define MT6397_VDRM_CON12 0x0390
+#define MT6397_VDRM_CON13 0x0392
+#define MT6397_VDRM_CON14 0x0394
+#define MT6397_VDRM_CON15 0x0396
+#define MT6397_VDRM_CON16 0x0398
+#define MT6397_VDRM_CON17 0x039A
+#define MT6397_VDRM_CON18 0x039C
+#define MT6397_BUCK_K_CON0 0x039E
+#define MT6397_BUCK_K_CON1 0x03A0
+#define MT6397_ANALDO_CON0 0x0400
+#define MT6397_ANALDO_CON1 0x0402
+#define MT6397_ANALDO_CON2 0x0404
+#define MT6397_ANALDO_CON3 0x0406
+#define MT6397_ANALDO_CON4 0x0408
+#define MT6397_ANALDO_CON5 0x040A
+#define MT6397_ANALDO_CON6 0x040C
+#define MT6397_ANALDO_CON7 0x040E
+#define MT6397_DIGLDO_CON0 0x0410
+#define MT6397_DIGLDO_CON1 0x0412
+#define MT6397_DIGLDO_CON2 0x0414
+#define MT6397_DIGLDO_CON3 0x0416
+#define MT6397_DIGLDO_CON4 0x0418
+#define MT6397_DIGLDO_CON5 0x041A
+#define MT6397_DIGLDO_CON6 0x041C
+#define MT6397_DIGLDO_CON7 0x041E
+#define MT6397_DIGLDO_CON8 0x0420
+#define MT6397_DIGLDO_CON9 0x0422
+#define MT6397_DIGLDO_CON10 0x0424
+#define MT6397_DIGLDO_CON11 0x0426
+#define MT6397_DIGLDO_CON12 0x0428
+#define MT6397_DIGLDO_CON13 0x042A
+#define MT6397_DIGLDO_CON14 0x042C
+#define MT6397_DIGLDO_CON15 0x042E
+#define MT6397_DIGLDO_CON16 0x0430
+#define MT6397_DIGLDO_CON17 0x0432
+#define MT6397_DIGLDO_CON18 0x0434
+#define MT6397_DIGLDO_CON19 0x0436
+#define MT6397_DIGLDO_CON20 0x0438
+#define MT6397_DIGLDO_CON21 0x043A
+#define MT6397_DIGLDO_CON22 0x043C
+#define MT6397_DIGLDO_CON23 0x043E
+#define MT6397_DIGLDO_CON24 0x0440
+#define MT6397_DIGLDO_CON25 0x0442
+#define MT6397_DIGLDO_CON26 0x0444
+#define MT6397_DIGLDO_CON27 0x0446
+#define MT6397_DIGLDO_CON28 0x0448
+#define MT6397_DIGLDO_CON29 0x044A
+#define MT6397_DIGLDO_CON30 0x044C
+#define MT6397_DIGLDO_CON31 0x044E
+#define MT6397_DIGLDO_CON32 0x0450
+#define MT6397_DIGLDO_CON33 0x045A
+#define MT6397_SPK_CON0 0x0600
+#define MT6397_SPK_CON1 0x0602
+#define MT6397_SPK_CON2 0x0604
+#define MT6397_SPK_CON3 0x0606
+#define MT6397_SPK_CON4 0x0608
+#define MT6397_SPK_CON5 0x060A
+#define MT6397_SPK_CON6 0x060C
+#define MT6397_SPK_CON7 0x060E
+#define MT6397_SPK_CON8 0x0610
+#define MT6397_SPK_CON9 0x0612
+#define MT6397_SPK_CON10 0x0614
+#define MT6397_SPK_CON11 0x0616
+#define MT6397_AUDDAC_CON0 0x0700
+#define MT6397_AUDBUF_CFG0 0x0702
+#define MT6397_AUDBUF_CFG1 0x0704
+#define MT6397_AUDBUF_CFG2 0x0706
+#define MT6397_AUDBUF_CFG3 0x0708
+#define MT6397_AUDBUF_CFG4 0x070A
+#define MT6397_IBIASDIST_CFG0 0x070C
+#define MT6397_AUDACCDEPOP_CFG0 0x070E
+#define MT6397_AUD_IV_CFG0 0x0710
+#define MT6397_AUDCLKGEN_CFG0 0x0712
+#define MT6397_AUDLDO_CFG0 0x0714
+#define MT6397_AUDLDO_CFG1 0x0716
+#define MT6397_AUDNVREGGLB_CFG0 0x0718
+#define MT6397_AUD_NCP0 0x071A
+#define MT6397_AUDPREAMP_CON0 0x071C
+#define MT6397_AUDADC_CON0 0x071E
+#define MT6397_AUDADC_CON1 0x0720
+#define MT6397_AUDADC_CON2 0x0722
+#define MT6397_AUDADC_CON3 0x0724
+#define MT6397_AUDADC_CON4 0x0726
+#define MT6397_AUDADC_CON5 0x0728
+#define MT6397_AUDADC_CON6 0x072A
+#define MT6397_AUDDIGMI_CON0 0x072C
+#define MT6397_AUDLSBUF_CON0 0x072E
+#define MT6397_AUDLSBUF_CON1 0x0730
+#define MT6397_AUDENCSPARE_CON0 0x0732
+#define MT6397_AUDENCCLKSQ_CON0 0x0734
+#define MT6397_AUDPREAMPGAIN_CON0 0x0736
+#define MT6397_ZCD_CON0 0x0738
+#define MT6397_ZCD_CON1 0x073A
+#define MT6397_ZCD_CON2 0x073C
+#define MT6397_ZCD_CON3 0x073E
+#define MT6397_ZCD_CON4 0x0740
+#define MT6397_ZCD_CON5 0x0742
+#define MT6397_NCP_CLKDIV_CON0 0x0744
+#define MT6397_NCP_CLKDIV_CON1 0x0746
+
+#endif /* __MFD_MT6397_REGISTERS_H__ */
--
2.1.4

2015-02-09 10:51:53

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 09/13] ARM: dts: mediatek: Enable clock support for Mediatek MT8135.

On Mon, Feb 09, 2015 at 11:47:21AM +0100, Sascha Hauer wrote:
> From: James Liao <[email protected]>
>
> This patch adds MT8135 clock controllers into device tree.
> @@ -86,6 +87,18 @@
> clock-frequency = <32000>;
> #clock-cells = <0>;
> };
> +
> + clk_null: clk_null {
> + compatible = "fixed-clock";
> + #clock-cells = <0>;
> + clock-frequency = <0>;
> + };
> +
> + clk26m: clk26m {
> + compatible = "fixed-clock";
> + #clock-cells = <0>;
> + clock-frequency = <26000000>;
> + };

Is this supposed to be here?

--
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

2015-02-09 11:25:15

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 09/13] ARM: dts: mediatek: Enable clock support for Mediatek MT8135.

On Mon, Feb 09, 2015 at 10:51:34AM +0000, Russell King - ARM Linux wrote:
> On Mon, Feb 09, 2015 at 11:47:21AM +0100, Sascha Hauer wrote:
> > From: James Liao <[email protected]>
> >
> > This patch adds MT8135 clock controllers into device tree.
> > @@ -86,6 +87,18 @@
> > clock-frequency = <32000>;
> > #clock-cells = <0>;
> > };
> > +
> > + clk_null: clk_null {
> > + compatible = "fixed-clock";
> > + #clock-cells = <0>;
> > + clock-frequency = <0>;
> > + };
> > +
> > + clk26m: clk26m {
> > + compatible = "fixed-clock";
> > + #clock-cells = <0>;
> > + clock-frequency = <26000000>;
> > + };
>
> Is this supposed to be here?

The clock support needs at least the clk26m clk. Do you think it should
be in another patch or not present at all?

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2015-02-09 11:27:38

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 09/13] ARM: dts: mediatek: Enable clock support for Mediatek MT8135.

On Mon, Feb 09, 2015 at 12:25:00PM +0100, Sascha Hauer wrote:
> On Mon, Feb 09, 2015 at 10:51:34AM +0000, Russell King - ARM Linux wrote:
> > On Mon, Feb 09, 2015 at 11:47:21AM +0100, Sascha Hauer wrote:
> > > From: James Liao <[email protected]>
> > >
> > > This patch adds MT8135 clock controllers into device tree.
> > > @@ -86,6 +87,18 @@
> > > clock-frequency = <32000>;
> > > #clock-cells = <0>;
> > > };
> > > +
> > > + clk_null: clk_null {
> > > + compatible = "fixed-clock";
> > > + #clock-cells = <0>;
> > > + clock-frequency = <0>;
> > > + };
> > > +
> > > + clk26m: clk26m {
> > > + compatible = "fixed-clock";
> > > + #clock-cells = <0>;
> > > + clock-frequency = <26000000>;
> > > + };
> >
> > Is this supposed to be here?
>
> The clock support needs at least the clk26m clk. Do you think it should
> be in another patch or not present at all?

I didn't see anything which referenced either clk_null or clk26m in this
patch. My main concern was that clk_null, but my concern grew when I
found that clk26m wasn't referenced either.

If the MT8135 needs it, shouldn't something in its description reference
this clock?

--
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

2015-02-09 11:44:08

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 09/13] ARM: dts: mediatek: Enable clock support for Mediatek MT8135.

On Mon, Feb 09, 2015 at 11:27:21AM +0000, Russell King - ARM Linux wrote:
> On Mon, Feb 09, 2015 at 12:25:00PM +0100, Sascha Hauer wrote:
> > On Mon, Feb 09, 2015 at 10:51:34AM +0000, Russell King - ARM Linux wrote:
> > > On Mon, Feb 09, 2015 at 11:47:21AM +0100, Sascha Hauer wrote:
> > > > From: James Liao <[email protected]>
> > > >
> > > > This patch adds MT8135 clock controllers into device tree.
> > > > @@ -86,6 +87,18 @@
> > > > clock-frequency = <32000>;
> > > > #clock-cells = <0>;
> > > > };
> > > > +
> > > > + clk_null: clk_null {
> > > > + compatible = "fixed-clock";
> > > > + #clock-cells = <0>;
> > > > + clock-frequency = <0>;
> > > > + };
> > > > +
> > > > + clk26m: clk26m {
> > > > + compatible = "fixed-clock";
> > > > + #clock-cells = <0>;
> > > > + clock-frequency = <26000000>;
> > > > + };
> > >
> > > Is this supposed to be here?
> >
> > The clock support needs at least the clk26m clk. Do you think it should
> > be in another patch or not present at all?
>
> I didn't see anything which referenced either clk_null or clk26m in this
> patch. My main concern was that clk_null, but my concern grew when I
> found that clk26m wasn't referenced either.
>
> If the MT8135 needs it, shouldn't something in its description reference
> this clock?

It's used in the code. The mt8135 clk code directly uses the names of
these three clocks as parent_names.

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2015-02-09 13:35:23

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 01/13] clk: dts: mediatek: add Mediatek MT8135 clock bindings

Am Montag, den 09.02.2015, 11:47 +0100 schrieb Sascha Hauer:
> From: James Liao <[email protected]>
>
> Document the device-tree binding of Mediatek MT8135 SoC, including
> TOPCKGEN, PLLs, INFRA and PERI clock controller.
>
> Signed-off-by: James Liao <[email protected]>
> Signed-off-by: Henry Chen <[email protected]>
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
> .../bindings/clock/mediatek,mt8135-clock.txt | 44 +++++
> include/dt-bindings/clock/mt8135-clk.h | 190 +++++++++++++++++++++
> 2 files changed, 234 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
> create mode 100644 include/dt-bindings/clock/mt8135-clk.h
>
> diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
> new file mode 100644
> index 0000000..1e3566f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt
> @@ -0,0 +1,44 @@
> +Mediatek MT8135 Clock Controller
> +
> +This binding uses the common clock binding:
> +Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +The Mediatek MT8135 clock controller generates and supplies clock to various
> +controllers within Mediatek MT8135 SoC.
> +
> +Required Properties:
> +
> +- compatible: should be one of following:
> + - "mediatek,mt8135-topckgen" : for topckgen clock controller of MT8135.
> + - "mediatek,mt8135-apmixedsys" : for apmixed_sys (PLLs) of MT8135.
> + - "mediatek,mt8135-infracfg" : for infra_sys clock controller of MT8135.
> + - "mediatek,mt8135-pericfg" : for peri_sys clock controller of MT8135.
> +
> +- reg: physical base address of the controller and length of memory mapped
> + region.
> +
> +- #clock-cells: should be 1.

After patch 3 ("clk: mediatek: Add reset controller support"), there's
another required property:

- #reset-cells: should be 1.

Patch 9 ("ARM: dts: mediatek: Enable clock support for Mediatek
MT8135.") already correctly includes these in the dtsi.

regards
Philipp

2015-02-09 13:35:55

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 03/13] clk: mediatek: Add reset controller support

Am Montag, den 09.02.2015, 11:47 +0100 schrieb Sascha Hauer:
> The pericfg and infracfg units also provide reset lines to several
> other SoC internal units. Add support for the reset controller.
>
> Signed-off-by: Sascha Hauer <[email protected]>

Acked-by: Philipp Zabel <[email protected]>

regards
Philipp

2015-02-13 07:42:17

by Tomasz Figa

[permalink] [raw]
Subject: Re: [PATCH 02/13] clk: mediatek: Add initial common clock support for Mediatek SoCs.

Hi,

Let me add some suggestions inline.

On Mon, Feb 9, 2015 at 7:47 PM, Sascha Hauer <[email protected]> wrote:
> From: James Liao <[email protected]>
>
> This patch adds common clock support for Mediatek SoCs, including plls,
> muxes and clock gates.

[snip]

> +static int mtk_cg_enable(struct clk_hw *hw)
> +{
> + mtk_cg_clr_bit(hw);
> +
> + return 0;
> +}
> +
> +static void mtk_cg_disable(struct clk_hw *hw)
> +{
> + mtk_cg_set_bit(hw);
> +}
> +
> +static int mtk_cg_enable_inv(struct clk_hw *hw)
> +{
> + mtk_cg_set_bit(hw);
> +
> + return 0;
> +}
> +
> +static void mtk_cg_disable_inv(struct clk_hw *hw)
> +{
> + mtk_cg_clr_bit(hw);
> +}

Instead of duplicating the ops, couldn't you add a flag or something
to mtk_clk_gate struct and then act appropriately in the ops? Also,
see below.

> +
> +const struct clk_ops mtk_clk_gate_ops_setclr = {
> + .is_enabled = mtk_cg_bit_is_cleared,
> + .enable = mtk_cg_enable,
> + .disable = mtk_cg_disable,
> +};
> +
> +const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
> + .is_enabled = mtk_cg_bit_is_set,
> + .enable = mtk_cg_enable_inv,
> + .disable = mtk_cg_disable_inv,
> +};
> +
> +struct clk *mtk_clk_register_gate(
> + const char *name,
> + const char *parent_name,
> + struct regmap *regmap,
> + int set_ofs,
> + int clr_ofs,
> + int sta_ofs,
> + u8 bit,
> + const struct clk_ops *ops)

Instead of passing the ops here you would have some flags or even just
a single bool inverted. Then the ops struct could be made static.

Also it would be nice to have a kerneldoc-style comment documenting
arguments of this function. Same thing applies to other structs added
by this and related patches and non-static functions.

also CodingStyle: I believe it is not kernel coding style to push
every argument to new line, even if few of them can fit one line.
Similar thing applies to other functions added by this and related
patches using this convention.

> +{
> + struct mtk_clk_gate *cg;
> + struct clk *clk;
> + struct clk_init_data init;
> +
> + cg = kzalloc(sizeof(*cg), GFP_KERNEL);
> + if (!cg)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.flags = CLK_SET_RATE_PARENT;
> + init.parent_names = parent_name ? &parent_name : NULL;
> + init.num_parents = parent_name ? 1 : 0;
> + init.ops = ops;
> +
> + cg->regmap = regmap;
> + cg->set_ofs = set_ofs;
> + cg->clr_ofs = clr_ofs;
> + cg->sta_ofs = sta_ofs;
> + cg->bit = bit;
> +
> + cg->hw.init = &init;
> +
> + clk = clk_register(NULL, &cg->hw);
> + if (IS_ERR(clk))
> + kfree(cg);
> +
> + return clk;
> +}
> diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
> new file mode 100644
> index 0000000..a44dcbf
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-gate.h
> @@ -0,0 +1,49 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>

Might not be necessary, but maybe the other people (all or some of
them) from signed-off-by should be added to this and other copyright
statements?

> + *
> + * 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.
> + */
> +
> +#ifndef __DRV_CLK_GATE_H
> +#define __DRV_CLK_GATE_H
> +
> +/*
> + * This is a private header file. DO NOT include it except clk-*.c.
> + */

I believe the above comment is unnecessary, because the file is
already located in drivers/clk/mediatek.

> +#include <linux/regmap.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +
> +struct mtk_clk_gate {

It would be nice to have a kerneldoc-style comment describing fields
of this struct.

> + struct clk_hw hw;
> + struct regmap *regmap;
> + int set_ofs;
> + int clr_ofs;
> + int sta_ofs;
> + u8 bit;
> +};
> +
> +#define to_clk_gate(_hw) container_of(_hw, struct mtk_clk_gate, hw)

I believe static inline is preferred to macros for such helpers, due
to increased type safety.

> +
> +extern const struct clk_ops mtk_clk_gate_ops_setclr;
> +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
> +
> +struct clk *mtk_clk_register_gate(
> + const char *name,
> + const char *parent_name,
> + struct regmap *regmap,
> + int set_ofs,
> + int clr_ofs,
> + int sta_ofs,
> + u8 bit,
> + const struct clk_ops *ops);
> +
> +#endif /* __DRV_CLK_GATE_H */
> diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
> new file mode 100644
> index 0000000..479857c
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mtk.c
> @@ -0,0 +1,155 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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/of.h>
> +#include <linux/of_address.h>
> +
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/clkdev.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +void mtk_init_factors(struct mtk_fixed_factor *clks, int num,
> + struct clk_onecell_data *clk_data)
> +{
> + int i;
> + struct clk *clk;
> +
> + for (i = 0; i < num; i++) {
> + struct mtk_fixed_factor *ff = &clks[i];
> +
> + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
> + CLK_SET_RATE_PARENT, ff->mult, ff->div);
> +
> + if (IS_ERR(clk)) {
> + pr_err("Failed to register clk %s: %ld\n",
> + ff->name, PTR_ERR(clk));
> + continue;
> + }
> +
> + if (clk_data)
> + clk_data->clks[ff->id] = clk;
> + }
> +}
> +
> +void mtk_init_clk_gates(struct regmap *regmap,
> + struct mtk_gate *clks, int num,
> + struct clk_onecell_data *clk_data)
> +{
> + int i;
> + struct clk *clk;
> +
> + for (i = 0; i < num; i++) {
> + struct mtk_gate *gate = &clks[i];
> +
> + clk = mtk_clk_register_gate(gate->name, gate->parent_name,
> + regmap,
> + gate->regs->set_ofs,
> + gate->regs->clr_ofs,
> + gate->regs->sta_ofs,
> + gate->shift, gate->ops);
> +
> + if (IS_ERR(clk)) {
> + pr_err("Failed to register clk %s: %ld\n",
> + gate->name, PTR_ERR(clk));
> + continue;
> + }
> +
> + if (clk_data)
> + clk_data->clks[gate->id] = clk;
> + }
> +}
> +
> +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
> +{
> + int i;
> + struct clk_onecell_data *clk_data;
> +
> + clk_data = kzalloc(sizeof(clk_data), GFP_KERNEL);

Shouldn't it be sizeof(*clk_data)?

> + if (!clk_data)
> + return NULL;
> +
> + clk_data->clks = kcalloc(clk_num, sizeof(struct clk *), GFP_KERNEL);

sizeof(*clk_data->clks)

> + if (!clk_data->clks) {
> + kfree(clk_data);
> + return NULL;
> + }
> +
> + clk_data->clk_num = clk_num;
> +
> + for (i = 0; i < clk_num; ++i)
> + clk_data->clks[i] = ERR_PTR(-ENOENT);
> +
> + return clk_data;
> +}
> +
> +struct clk *mtk_clk_register_mux(
> + const char *name,
> + const char **parent_names,
> + u8 num_parents,
> + void __iomem *base_addr,
> + u8 shift,
> + u8 width,
> + u8 gate_bit,
> + spinlock_t *lock)
> +{
> + struct clk *clk;
> + struct clk_mux *mux;
> + struct clk_gate *gate = NULL;
> + struct clk_hw *gate_hw = NULL;
> + const struct clk_ops *gate_ops = NULL;
> + u32 mask = BIT(width) - 1;
> +
> + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);

sizeof(*mux)

> + if (!mux)
> + return ERR_PTR(-ENOMEM);
> +
> + mux->reg = base_addr;
> + mux->mask = mask;
> + mux->shift = shift;
> + mux->flags = 0;

Flags field was already zeroed by kzalloc().

> + mux->lock = lock;
> +
> + if (gate_bit <= MAX_MUX_GATE_BIT) {
> + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);

sizeof(*gate)

> + if (!gate) {
> + kfree(mux);

Please use goto style error path below the main code instead of
duplicating roll-back actions in every error check.

> + return ERR_PTR(-ENOMEM);
> + }
> +
> + gate->reg = base_addr;
> + gate->bit_idx = gate_bit;
> + gate->flags = CLK_GATE_SET_TO_DISABLE;
> + gate->lock = lock;
> +
> + gate_hw = &gate->hw;
> + gate_ops = &clk_gate_ops;
> + }
> +
> + clk = clk_register_composite(NULL, name, parent_names, num_parents,
> + &mux->hw, &clk_mux_ops,
> + NULL, NULL,
> + gate_hw, gate_ops,
> + CLK_SET_RATE_PARENT);
> +
> + if (IS_ERR(clk)) {
> + kfree(gate);
> + kfree(mux);
> + }
> +
> + return clk;
> +}
> diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
> new file mode 100644
> index 0000000..35cf9a3
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mtk.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef __DRV_CLK_MTK_H
> +#define __DRV_CLK_MTK_H
> +
> +/*
> + * This is a private header file. DO NOT include it except clk-*.c.
> + */
> +
> +#include <linux/regmap.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +
> +#define MAX_MUX_GATE_BIT 31
> +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1)

It might be a good idea to describe what this value is used for.

> +
> +struct mtk_fixed_factor {
> + int id;
> + const char *name;
> + const char *parent_name;
> + int mult;
> + int div;
> +};
> +
> +#define FACTOR(_id, _name, _parent, _mult, _div) { \
> + .id = _id, \
> + .name = _name, \
> + .parent_name = _parent, \
> + .mult = _mult, \
> + .div = _div, \
> + }
> +
> +extern void mtk_init_factors(struct mtk_fixed_factor *clks, int num,
> + struct clk_onecell_data *clk_data);
> +
> +struct mtk_mux {
> + int id;
> + const char *name;
> + uint32_t reg;
> + int shift;
> + int width;
> + int gate;
> + const char **parent_names;
> + int num_parents;
> +};
> +
> +#define MUX(_id, _name, _parents, _reg, _shift, _width, _gate) { \
> + .id = _id, \
> + .name = _name, \
> + .reg = _reg, \
> + .shift = _shift, \
> + .width = _width, \
> + .gate = _gate, \
> + .parent_names = (const char **)_parents, \

Hmm, it doesn't sound like a good idea to hide casts like this inside
a macro. Anyway, is this cast even necessary? Your _parents argument
to this macro should be always of correct type.

> + .num_parents = ARRAY_SIZE(_parents), \
> + }
> +
> +struct mtk_pll {
> + int id;
> + const char *name;
> + const char *parent_name;
> + uint32_t reg;
> + uint32_t pwr_reg;
> + uint32_t en_mask;
> + unsigned int flags;
> + const struct clk_ops *ops;
> +};
> +
> +#define PLL(_id, _name, _parent, _reg, _pwr_reg, _en_mask, _flags, _ops) { \
> + .id = _id, \
> + .name = _name, \
> + .parent_name = _parent, \
> + .reg = _reg, \
> + .pwr_reg = _pwr_reg, \
> + .en_mask = _en_mask, \
> + .flags = _flags, \
> + .ops = _ops, \
> + }
> +
> +struct mtk_gate_regs {
> + u32 sta_ofs;
> + u32 clr_ofs;
> + u32 set_ofs;
> +};
> +
> +struct mtk_gate {
> + int id;
> + const char *name;
> + const char *parent_name;
> + struct mtk_gate_regs *regs;
> + int shift;
> + const struct clk_ops *ops;
> +};
> +
> +#define GATE(_id, _name, _parent, _regs, _shift, _ops) { \
> + .id = _id, \
> + .name = _name, \
> + .parent_name = _parent, \
> + .regs = &_regs, \

Also hiding operators like & inside macros like this doesn't help with
code readability and it doesn't cost that much to just add & when
calling this macro.

> + .shift = _shift, \
> + .ops = _ops, \
> + }
> +
> +void mtk_init_clk_gates(struct regmap *regmap,
> + struct mtk_gate *clks, int num,
> + struct clk_onecell_data *clk_data);
> +
> +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
> +
> +struct clk *mtk_clk_register_mux(
> + const char *name,
> + const char **parent_names,
> + u8 num_parents,
> + void __iomem *base_addr,
> + u8 shift,
> + u8 width,
> + u8 gate_bit,
> + spinlock_t *lock);
> +
> +#endif /* __DRV_CLK_MTK_H */
> diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
> new file mode 100644
> index 0000000..59dee83
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-pll.c
> @@ -0,0 +1,63 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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/io.h>
> +#include <linux/slab.h>
> +#include <linux/clkdev.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-pll.h"
> +
> +struct clk *mtk_clk_register_pll(
> + const char *name,
> + const char *parent_name,
> + u32 *base_addr,
> + u32 *pwr_addr,
> + u32 en_mask,
> + u32 flags,
> + const struct clk_ops *ops,
> + spinlock_t *lock)
> +{
> + struct mtk_clk_pll *pll;
> + struct clk_init_data init;
> + struct clk *clk;
> +
> + pr_debug("%s(): name: %s\n", __func__, name);
> +
> + if (!lock)
> + return ERR_PTR(-EINVAL);

Hmm, this check seems to be slightly inconsistent. Why you need to
check lock, but not name, parent_name and other arguments of this
function? Also bailing out without any error message isn't really the
best practice.

Anyway, if this function is defined to require the lock argument to be
non-NULL then this is not a natural error condition but rather a
mistake of the author of code calling this function with lock == NULL.
IMHO either BUG_ON(!lock) or just remove this check, but I'd like to
know Mike's thoughts on this before you make this change.

> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + pll->base_addr = base_addr;
> + pll->pwr_addr = pwr_addr;
> + pll->en_mask = en_mask;
> + pll->flags = flags;
> + pll->lock = lock;
> + pll->hw.init = &init;
> +
> + init.name = name;
> + init.ops = ops;
> + init.parent_names = &parent_name;
> + init.num_parents = 1;
> +
> + clk = clk_register(NULL, &pll->hw);
> +
> + if (IS_ERR(clk))
> + kfree(pll);
> +
> + return clk;
> +}
> diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
> new file mode 100644
> index 0000000..341d2fe
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-pll.h
> @@ -0,0 +1,52 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef __DRV_CLK_PLL_H
> +#define __DRV_CLK_PLL_H
> +
> +/*
> + * This is a private header file. DO NOT include it except clk-*.c.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +
> +struct mtk_clk_pll {
> + struct clk_hw hw;
> + void __iomem *base_addr;
> + void __iomem *pwr_addr;
> + u32 en_mask;
> + u32 flags;
> + spinlock_t *lock;
> +};
> +
> +#define to_mtk_clk_pll(_hw) container_of(_hw, struct mtk_clk_pll, hw)

Static inline please.

Best regards,
Tomasz

2015-02-13 09:57:17

by Tomasz Figa

[permalink] [raw]
Subject: Re: [PATCH 06/13] clk: mediatek: Add basic clocks for Mediatek MT8173.

Please find my comments inline.

On Mon, Feb 9, 2015 at 7:47 PM, Sascha Hauer <[email protected]> wrote:
> From: James Liao <[email protected]>
>
> This patch adds basic clocks for MT8173, including TOPCKGEN, PLLs,
> INFRA and PERI clocks.
>
> Signed-off-by: James Liao <[email protected]>
> Signed-off-by: Henry Chen <[email protected]>
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
> drivers/clk/mediatek/Makefile | 1 +
> drivers/clk/mediatek/clk-mt8173-pll.c | 807 +++++++++++++++++++++++++
> drivers/clk/mediatek/clk-mt8173-pll.h | 14 +
> drivers/clk/mediatek/clk-mt8173.c | 1035 +++++++++++++++++++++++++++++++++
> 4 files changed, 1857 insertions(+)
> create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.c
> create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.h
> create mode 100644 drivers/clk/mediatek/clk-mt8173.c
>
> diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
> index afb52e5..e030416 100644
> --- a/drivers/clk/mediatek/Makefile
> +++ b/drivers/clk/mediatek/Makefile
> @@ -1,3 +1,4 @@
> obj-y += clk-mtk.o clk-pll.o clk-gate.o
> obj-$(CONFIG_RESET_CONTROLLER) += reset.o
> obj-y += clk-mt8135.o clk-mt8135-pll.o
> +obj-y += clk-mt8173.o clk-mt8173-pll.o
> diff --git a/drivers/clk/mediatek/clk-mt8173-pll.c b/drivers/clk/mediatek/clk-mt8173-pll.c
> new file mode 100644
> index 0000000..9f6f821
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt8173-pll.c
> @@ -0,0 +1,807 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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/io.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/clkdev.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-pll.h"
> +#include "clk-mt8173-pll.h"
> +
> +#define PLL_BASE_EN BIT(0)
> +#define PLL_PWR_ON BIT(0)
> +#define PLL_ISO_EN BIT(1)
> +#define PLL_PCW_CHG BIT(31)
> +#define RST_BAR_MASK BIT(24)
> +#define AUDPLL_TUNER_EN BIT(31)
> +
> +static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 };

It might be nice to have a comment what this array is for and how the
values were calculated.

> +
> +static u32 mtk_calc_pll_vco_freq(
> + u32 fin,
> + u32 pcw,
> + u32 vcodivsel,
> + u32 prediv,
> + u32 pcwfbits)
> +{
> + /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */
> + u64 vco = fin;
> + u8 c = 0;
> +
> + vco = vco * pcw * vcodivsel;

Could you use here (u64)fin directly for increased readability and
drop the initialization of vco?

> + do_div(vco, prediv);
> +
> + if (vco & GENMASK(pcwfbits - 1, 0))
> + c = 1;

What is c? Could the variable has a more meaningful name?

> +
> + vco >>= pcwfbits;
> +
> + if (c)
> + ++vco;
> +
> + return (u32)vco;
> +}
> +
> +static u32 mtk_freq_limit(u32 freq)
> +{
> + static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */

3 GHz probably? Could you define (if not defined somewhere already) a
macro for GHZ and write this as 3 * GHZ?

> + static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 62.5 MHz */

Why don't you write it as 62500 * KHZ or 62 * MHZ + 500 * KHZ?

> +
> + if (freq <= freq_min)
> + freq = freq_min + 16;

Could you explain what's happening here? Where does the 16 come from
and why it is not defined as a macro?

> + else if (freq > freq_max)
> + freq = freq_max;
> +
> + return freq;
> +}
> +
> +static int mtk_calc_pll_freq_cfg(
> + u32 *pcw,
> + u32 *postdiv_idx,
> + u32 freq,
> + u32 fin,
> + int pcwfbits)
> +{
> + static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
> + static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */
> + static const u64 postdiv[] = { 1, 2, 4, 8, 16 };
> + u64 n_info;
> + u32 idx;
> +
> + /* search suitable postdiv */
> + for (idx = *postdiv_idx;
> + idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min;
> + idx++)
> + ;

Please document the arguments of this function. It is not obvious why
the value at postdiv_idx is used as starting point, even though this
pointer is also used to store the output value...

> +
> + if (idx >= ARRAY_SIZE(postdiv))
> + return -EINVAL; /* freq is out of range (too low) */
> + else if (postdiv[idx] * freq > freq_max)
> + return -EINVAL; /* freq is out of range (too high) */
> +
> + /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */
> + n_info = (postdiv[idx] * freq) << pcwfbits;
> + do_div(n_info, fin);
> +
> + *postdiv_idx = idx;
> + *pcw = (u32)n_info;
> +
> + return 0;
> +}
> +
> +static int mtk_clk_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> +
> + return (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0;
> +}
> +
> +static int mtk_clk_pll_prepare(struct clk_hw *hw)

Hmm, contents of this function don't seem to sleep. Maybe this should
be enable instead of prepare?

> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + u32 r;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
> + writel_relaxed(r, pll->pwr_addr);
> + wmb(); /* sync write before delay */
> + udelay(1);
> +
> + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
> + writel_relaxed(r, pll->pwr_addr);
> + wmb(); /* sync write before delay */
> + udelay(1);
> +
> + r = readl_relaxed(pll->base_addr) | pll->en_mask;
> + writel_relaxed(r, pll->base_addr);
> + wmb(); /* sync write before delay */
> + udelay(20);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
> + writel_relaxed(r, pll->base_addr);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +
> + return 0;
> +}
> +
> +static void mtk_clk_pll_unprepare(struct clk_hw *hw)

Similarly to prepare, maybe you should consider to implement disable
instead of unprepare.

> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + u32 r;
> +
> + if (pll->flags & PLL_AO)
> + return;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
> + writel_relaxed(r, pll->base_addr);
> + }
> +
> + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
> + writel_relaxed(r, pll->base_addr);
> +
> + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
> + writel_relaxed(r, pll->pwr_addr);
> +
> + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
> + writel_relaxed(r, pll->pwr_addr);
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +static long mtk_clk_pll_round_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *prate)
> +{
> + u32 pcwfbits = 14;
> + u32 pcw = 0;
> + u32 postdiv = 0;
> + u32 r;
> +
> + *prate = *prate ? *prate : 26000000;
> + rate = mtk_freq_limit(rate);
> + mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
> +
> + r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
> + r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
> +
> + return r;
> +}
> +
> +#define SDM_PLL_POSTDIV_H 6
> +#define SDM_PLL_POSTDIV_L 4
> +#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L)
> +#define SDM_PLL_PCW_H 20
> +#define SDM_PLL_PCW_L 0
> +#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L)
> +
> +static unsigned long mtk_clk_sdm_pll_recalc_rate(
> + struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> +
> + u32 con0 = readl_relaxed(pll->base_addr);
> + u32 con1 = readl_relaxed(pll->base_addr + 4);
> +
> + u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L;
> + u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L;
> + u32 pcwfbits = 14;
> +
> + u32 vco_freq;
> + unsigned long r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> +
> + vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
> + r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
> +
> + return r;
> +}
> +
> +static void mtk_clk_sdm_pll_set_rate_regs(
> + struct clk_hw *hw,
> + u32 pcw,
> + u32 postdiv_idx)
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con1_addr = pll->base_addr + 4;
> + u32 con0;
> + u32 con1;
> + u32 pll_en;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + con0 = readl_relaxed(con0_addr);
> + con1 = readl_relaxed(con1_addr);
> +
> + pll_en = con0 & PLL_BASE_EN;
> +
> + /* set postdiv */
> + con0 &= ~SDM_PLL_POSTDIV_MASK;
> + con0 |= postdiv_idx << SDM_PLL_POSTDIV_L;
> + writel_relaxed(con0, con0_addr);
> +
> + /* set pcw */
> + con1 &= ~SDM_PLL_PCW_MASK;
> + con1 |= pcw << SDM_PLL_PCW_L;
> +
> + if (pll_en)
> + con1 |= PLL_PCW_CHG;
> +
> + writel_relaxed(con1, con1_addr);
> +
> + if (pll_en) {
> + wmb(); /* sync write before delay */
> + udelay(20);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +static int mtk_clk_sdm_pll_set_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + u32 pcwfbits = 14;
> + u32 pcw = 0;
> + u32 postdiv_idx = 0;
> + int r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> + parent_rate, pcwfbits);
> +
> + if (r == 0)
> + mtk_clk_sdm_pll_set_rate_regs(hw, pcw, postdiv_idx);
> +
> + return r;
> +}
> +
> +const struct clk_ops mt8173_sdm_pll_ops = {
> + .is_enabled = mtk_clk_pll_is_enabled,
> + .prepare = mtk_clk_pll_prepare,
> + .unprepare = mtk_clk_pll_unprepare,
> + .recalc_rate = mtk_clk_sdm_pll_recalc_rate,
> + .round_rate = mtk_clk_pll_round_rate,
> + .set_rate = mtk_clk_sdm_pll_set_rate,
> +};
> +
> +#define ARM_PLL_POSTDIV_H 26
> +#define ARM_PLL_POSTDIV_L 24
> +#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L)
> +#define ARM_PLL_PCW_H 20
> +#define ARM_PLL_PCW_L 0
> +#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L)
> +
> +static unsigned long mtk_clk_arm_pll_recalc_rate(
> + struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> +
> + u32 con1 = readl_relaxed(pll->base_addr + 4);
> +
> + u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L;
> + u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L;
> + u32 pcwfbits = 14;
> +
> + u32 vco_freq;
> + unsigned long r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> +
> + vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
> + r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
> +
> + return r;
> +}
> +
> +static void mtk_clk_arm_pll_set_rate_regs(
> + struct clk_hw *hw,
> + u32 pcw,
> + u32 postdiv_idx)
> +
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con1_addr = pll->base_addr + 4;
> + u32 con0;
> + u32 con1;
> + u32 pll_en;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + con0 = readl_relaxed(con0_addr);
> + con1 = readl_relaxed(con1_addr);
> +
> + pll_en = con0 & PLL_BASE_EN;
> +
> + /* postdiv */
> + con1 &= ~ARM_PLL_POSTDIV_MASK;
> + con1 |= postdiv_idx << ARM_PLL_POSTDIV_L;
> +
> + /* pcw */
> + con1 &= ~ARM_PLL_PCW_MASK;
> + con1 |= pcw << ARM_PLL_PCW_L;
> +
> + if (pll_en)
> + con1 |= PLL_PCW_CHG;
> +
> + writel_relaxed(con1, con1_addr);
> +
> + if (pll_en) {
> + wmb(); /* sync write before delay */
> + udelay(20);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +static int mtk_clk_arm_pll_set_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + u32 pcwfbits = 14;
> + u32 pcw = 0;
> + u32 postdiv_idx = 0;
> + int r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> + parent_rate, pcwfbits);
> +
> + if (r == 0)
> + mtk_clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
> +
> + return r;
> +}
> +
> +const struct clk_ops mt8173_arm_pll_ops = {
> + .is_enabled = mtk_clk_pll_is_enabled,
> + .prepare = mtk_clk_pll_prepare,
> + .unprepare = mtk_clk_pll_unprepare,

Uhh, this is incorrect. If you provide prepare+unprepare, you also
need to provide is_prepared, not is_enabled. However, considering my
comments above, it should be possible to use enable+disable instead.

> + .recalc_rate = mtk_clk_arm_pll_recalc_rate,
> + .round_rate = mtk_clk_pll_round_rate,
> + .set_rate = mtk_clk_arm_pll_set_rate,
> +};
> +
> +static long mtk_clk_mm_pll_round_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *prate)
> +{
> + u32 pcwfbits = 14;
> + u32 pcw = 0;
> + u32 postdiv = 0;
> + u32 r;
> +
> + if (rate <= 702000000)
> + postdiv = 2;
> +
> + *prate = *prate ? *prate : 26000000;

I feel like it wouldn't really be a bad idea to define all the numeric
constants as macros.

> + rate = mtk_freq_limit(rate);
> + mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
> +
> + r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
> + r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
> +
> + return r;
> +}
> +
> +static int mtk_clk_mm_pll_set_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + u32 pcwfbits = 14;
> + u32 pcw = 0;
> + u32 postdiv_idx = 0;
> + int r;
> +
> + if (rate <= 702000000)
> + postdiv_idx = 2;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> + parent_rate, pcwfbits);
> +
> + if (r == 0)
> + mtk_clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
> +
> + return r;
> +}
> +
> +const struct clk_ops mt8173_mm_pll_ops = {
> + .is_enabled = mtk_clk_pll_is_enabled,
> + .prepare = mtk_clk_pll_prepare,
> + .unprepare = mtk_clk_pll_unprepare,

Ditto.

> + .recalc_rate = mtk_clk_arm_pll_recalc_rate,
> + .round_rate = mtk_clk_mm_pll_round_rate,
> + .set_rate = mtk_clk_mm_pll_set_rate,
> +};
> +
> +static int mtk_clk_univ_pll_prepare(struct clk_hw *hw)
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + u32 r;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + r = readl_relaxed(pll->base_addr) | pll->en_mask;
> + writel_relaxed(r, pll->base_addr);
> + wmb(); /* sync write before delay */
> + udelay(20);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
> + writel_relaxed(r, pll->base_addr);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +
> + return 0;
> +}
> +
> +static void mtk_clk_univ_pll_unprepare(struct clk_hw *hw)
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + u32 r;
> +
> + if (pll->flags & PLL_AO)
> + return;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
> + writel_relaxed(r, pll->base_addr);
> + }
> +
> + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
> + writel_relaxed(r, pll->base_addr);
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +#define UNIV_PLL_POSTDIV_H 6
> +#define UNIV_PLL_POSTDIV_L 4
> +#define UNIV_PLL_POSTDIV_MASK GENMASK(UNIV_PLL_POSTDIV_H, UNIV_PLL_POSTDIV_L)
> +#define UNIV_PLL_FBKDIV_H 20
> +#define UNIV_PLL_FBKDIV_L 14
> +#define UNIV_PLL_FBKDIV_MASK GENMASK(UNIV_PLL_FBKDIV_H, UNIV_PLL_FBKDIV_L)
> +
> +static unsigned long mtk_clk_univ_pll_recalc_rate(
> + struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> +
> + u32 con0 = readl_relaxed(pll->base_addr);
> + u32 con1 = readl_relaxed(pll->base_addr + 4);
> +
> + u32 fbkdiv = (con1 & UNIV_PLL_FBKDIV_MASK) >> UNIV_PLL_FBKDIV_L;
> + u32 posdiv = (con0 & UNIV_PLL_POSTDIV_MASK) >> UNIV_PLL_POSTDIV_L;
> +
> + u32 vco_freq;
> + unsigned long r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> +
> + vco_freq = parent_rate * fbkdiv;
> + r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
> +
> + return r;
> +}
> +
> +static void mtk_clk_univ_pll_set_rate_regs(
> + struct clk_hw *hw,
> + u32 pcw,
> + u32 postdiv_idx)
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con1_addr = pll->base_addr + 4;
> + u32 con0;
> + u32 con1;
> + u32 pll_en;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + con0 = readl_relaxed(con0_addr);
> + con1 = readl_relaxed(con1_addr);
> +
> + pll_en = con0 & PLL_BASE_EN;
> +
> + /* postdiv */
> + con0 &= ~UNIV_PLL_POSTDIV_MASK;
> + con0 |= postdiv_idx << UNIV_PLL_POSTDIV_L;
> +
> + /* fkbdiv */
> + con1 &= ~UNIV_PLL_FBKDIV_MASK;
> + con1 |= pcw << UNIV_PLL_FBKDIV_L;
> +
> + writel_relaxed(con0, con0_addr);
> + writel_relaxed(con1, con1_addr);
> +
> + if (pll_en) {
> + wmb(); /* sync write before delay */

The comment should say why, not what, because you can easily see that
from the code (wmb() before udelay(20) obviously can't be anything
else than "sync write before delay").

> + udelay(20);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +static long mtk_clk_univ_pll_round_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *prate)
> +{
> + u32 pcwfbits = 0;
> + u32 pcw = 0;
> + u32 postdiv = 0;
> + u32 r;
> +
> + *prate = *prate ? *prate : 26000000;
> + rate = mtk_freq_limit(rate);
> + mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
> +
> + r = *prate * pcw;
> + r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
> +
> + return r;
> +}
> +
> +static int mtk_clk_univ_pll_set_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + u32 pcwfbits = 0;
> + u32 pcw = 0;
> + u32 postdiv_idx = 0;
> + int r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> + parent_rate, pcwfbits);
> +
> + if (r == 0)

I wonder if you shouldn't consider adding an error message to opposite case.

> + mtk_clk_univ_pll_set_rate_regs(hw, pcw, postdiv_idx);
> +
> + return r;
> +}
> +
> +const struct clk_ops mt8173_univ_pll_ops = {
> + .is_enabled = mtk_clk_pll_is_enabled,
> + .prepare = mtk_clk_univ_pll_prepare,
> + .unprepare = mtk_clk_univ_pll_unprepare,
> + .recalc_rate = mtk_clk_univ_pll_recalc_rate,
> + .round_rate = mtk_clk_univ_pll_round_rate,
> + .set_rate = mtk_clk_univ_pll_set_rate,
> +};
> +
> +static int mtk_clk_aud_pll_prepare(struct clk_hw *hw)
> +{
> + unsigned long flags = 0;

No need to initialize.

> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con2_addr = pll->base_addr + 8;

A macro for the offset would look better.

> + u32 r;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
> + writel_relaxed(r, pll->pwr_addr);
> + wmb(); /* sync write before delay */

Why? And couldn't you use writel() instead of writel_relaxed() + wmb()?

> + udelay(1);
> +
> + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
> + writel_relaxed(r, pll->pwr_addr);
> + wmb(); /* sync write before delay */

Ditto.

> + udelay(1);
> +
> + r = readl_relaxed(con0_addr) | pll->en_mask;
> + writel_relaxed(r, con0_addr);
> +
> + r = readl_relaxed(con2_addr) | AUDPLL_TUNER_EN;
> + writel_relaxed(r, con2_addr);
> + wmb(); /* sync write before delay */

Ditto.

> + udelay(20);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(con0_addr) | RST_BAR_MASK;
> + writel_relaxed(r, con0_addr);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +
> + return 0;
> +}
> +
> +static void mtk_clk_aud_pll_unprepare(struct clk_hw *hw)
> +{
> + unsigned long flags = 0;
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con2_addr = pll->base_addr + 8;
> + u32 r;
> +
> + if (pll->flags & PLL_AO)
> + return;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + if (pll->flags & HAVE_RST_BAR) {
> + r = readl_relaxed(con0_addr) & ~RST_BAR_MASK;
> + writel_relaxed(r, con0_addr);
> + }
> +
> + r = readl_relaxed(con2_addr) & ~AUDPLL_TUNER_EN;
> + writel_relaxed(r, con2_addr);
> +
> + r = readl_relaxed(con0_addr) & ~PLL_BASE_EN;
> + writel_relaxed(r, con0_addr);
> +
> + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
> + writel_relaxed(r, pll->pwr_addr);
> +
> + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
> + writel_relaxed(r, pll->pwr_addr);
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +#define AUD_PLL_POSTDIV_H 6
> +#define AUD_PLL_POSTDIV_L 4
> +#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L)
> +#define AUD_PLL_PCW_H 30
> +#define AUD_PLL_PCW_L 0
> +#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L)
> +
> +static unsigned long mtk_clk_aud_pll_recalc_rate(
> + struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> +
> + u32 con0 = readl_relaxed(pll->base_addr);
> + u32 con1 = readl_relaxed(pll->base_addr + 4);
> +
> + u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L;
> + u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L;
> + u32 pcwfbits = 24;
> +
> + u32 vco_freq;
> + unsigned long r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> +
> + vco_freq = mtk_calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
> + r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
> +
> + return r;
> +}
> +
> +static void mtk_clk_aud_pll_set_rate_regs(
> + struct clk_hw *hw,
> + u32 pcw,
> + u32 postdiv_idx)
> +{
> + unsigned long flags = 0;
> + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> + void __iomem *con0_addr = pll->base_addr;
> + void __iomem *con1_addr = pll->base_addr + 4;
> + void __iomem *con2_addr = pll->base_addr + 8;
> + u32 con0;
> + u32 con1;
> + u32 pll_en;
> +
> + spin_lock_irqsave(pll->lock, flags);
> +
> + con0 = readl_relaxed(con0_addr);
> + con1 = readl_relaxed(con1_addr);
> +
> + pll_en = con0 & PLL_BASE_EN;
> +
> + /* set postdiv */
> + con0 &= ~AUD_PLL_POSTDIV_MASK;
> + con0 |= postdiv_idx << AUD_PLL_POSTDIV_L;
> + writel_relaxed(con0, con0_addr);
> +
> + /* set pcw */
> + con1 &= ~AUD_PLL_PCW_MASK;
> + con1 |= pcw << AUD_PLL_PCW_L;
> +
> + if (pll_en)
> + con1 |= PLL_PCW_CHG;
> +
> + writel_relaxed(con1, con1_addr);
> + writel_relaxed(con1 + 1, con2_addr);
> +
> + if (pll_en) {
> + wmb(); /* sync write before delay */

Same as above.

> + udelay(20);
> + }
> +
> + spin_unlock_irqrestore(pll->lock, flags);
> +}
> +
> +static long mtk_clk_aud_pll_round_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *prate)
> +{
> + u32 pcwfbits = 24;
> + u32 pcw = 0;
> + u32 postdiv = 0;
> + u32 r;
> +
> + *prate = *prate ? *prate : 26000000;
> + rate = mtk_freq_limit(rate);
> + mtk_calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
> +
> + r = mtk_calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
> + r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
> +
> + return r;
> +}
> +
> +static int mtk_clk_aud_pll_set_rate(
> + struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + u32 pcwfbits = 24;
> + u32 pcw = 0;
> + u32 postdiv_idx = 0;
> + int r;
> +
> + parent_rate = parent_rate ? parent_rate : 26000000;
> + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> + parent_rate, pcwfbits);
> +
> + if (r == 0)
> + mtk_clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx);
> +
> + return r;
> +}
> +
> +const struct clk_ops mt8173_aud_pll_ops = {
> + .is_enabled = mtk_clk_pll_is_enabled,
> + .prepare = mtk_clk_aud_pll_prepare,
> + .unprepare = mtk_clk_aud_pll_unprepare,
> + .recalc_rate = mtk_clk_aud_pll_recalc_rate,
> + .round_rate = mtk_clk_aud_pll_round_rate,
> + .set_rate = mtk_clk_aud_pll_set_rate,
> +};
> diff --git a/drivers/clk/mediatek/clk-mt8173-pll.h b/drivers/clk/mediatek/clk-mt8173-pll.h
> new file mode 100644
> index 0000000..663ab4b
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt8173-pll.h
> @@ -0,0 +1,14 @@
> +#ifndef __DRV_CLK_MT8173_PLL_H
> +#define __DRV_CLK_MT8173_PLL_H
> +
> +/*
> + * This is a private header file. DO NOT include it except clk-*.c.
> + */

I'd say this comment is redundant, because this file is already
private to mediatek clock code by how it is located in
drivers/clk/mediatek.

> +
> +extern const struct clk_ops mt8173_sdm_pll_ops;
> +extern const struct clk_ops mt8173_arm_pll_ops;
> +extern const struct clk_ops mt8173_mm_pll_ops;
> +extern const struct clk_ops mt8173_univ_pll_ops;
> +extern const struct clk_ops mt8173_aud_pll_ops;
> +
> +#endif /* __DRV_CLK_MT8173_PLL_H */
> diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
> new file mode 100644
> index 0000000..d75e591
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt8173.c
> @@ -0,0 +1,1035 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: James Liao <[email protected]>
> + *
> + * 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/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/mfd/syscon.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-pll.h"
> +#include "clk-gate.h"
> +#include "clk-mt8173-pll.h"
> +
> +#include <dt-bindings/clock/mt8173-clk.h>
> +
> +/* ROOT */
> +#define clk_null "clk_null"
> +#define clk26m "clk26m"
> +#define clk32k "clk32k"

Hmm, what's this? What's the purpose of defining the same string, just
without the quotation marks?

Best regards,
Tomasz

2015-02-13 12:06:19

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 02/13] clk: mediatek: Add initial common clock support for Mediatek SoCs.


Hi Tomasz,

> > +static void mtk_cg_disable(struct clk_hw *hw)
> > +{
> > + mtk_cg_set_bit(hw);
> > +}
> > +
> > +static int mtk_cg_enable_inv(struct clk_hw *hw)
> > +{
> > + mtk_cg_set_bit(hw);
> > +
> > + return 0;
> > +}
> > +
> > +static void mtk_cg_disable_inv(struct clk_hw *hw)
> > +{
> > + mtk_cg_clr_bit(hw);
> > +}
>
> Instead of duplicating the ops, couldn't you add a flag or something
> to mtk_clk_gate struct and then act appropriately in the ops? Also,
> see below.

I prefer duplicating the ops. It makes the functions simpler without
ifs.

> > diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
> > new file mode 100644
> > index 0000000..a44dcbf
> > --- /dev/null
> > +++ b/drivers/clk/mediatek/clk-gate.h
> > @@ -0,0 +1,49 @@
> > +/*
> > + * Copyright (c) 2014 MediaTek Inc.
> > + * Author: James Liao <[email protected]>
>
> Might not be necessary, but maybe the other people (all or some of
> them) from signed-off-by should be added to this and other copyright
> statements?

I rather do not want to update these copyrights frequently. Otherwise we
would see a lot of patches with an extra hunk changing the copyrights.
I'm glad we left that behind and look at the git history instead.
The above is the original author. I don't want to remove him, but I also
do not want to add every other person who touched that file.

The other stuff will be fixed in the next round. Thanks for reviewing.

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2015-02-13 13:22:26

by Tomasz Figa

[permalink] [raw]
Subject: Re: [PATCH 02/13] clk: mediatek: Add initial common clock support for Mediatek SoCs.

Hi Sascha,

On Fri, Feb 13, 2015 at 9:06 PM, Sascha Hauer <[email protected]> wrote:
>
> Hi Tomasz,
>
>> > +static void mtk_cg_disable(struct clk_hw *hw)
>> > +{
>> > + mtk_cg_set_bit(hw);
>> > +}
>> > +
>> > +static int mtk_cg_enable_inv(struct clk_hw *hw)
>> > +{
>> > + mtk_cg_set_bit(hw);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static void mtk_cg_disable_inv(struct clk_hw *hw)
>> > +{
>> > + mtk_cg_clr_bit(hw);
>> > +}
>>
>> Instead of duplicating the ops, couldn't you add a flag or something
>> to mtk_clk_gate struct and then act appropriately in the ops? Also,
>> see below.
>
> I prefer duplicating the ops. It makes the functions simpler without
> ifs.

I meant something else. Compared to ifs I'd prefer duplicated ops too.

is_enabled()
{
status = regmap_read() ^ (inverted << shift);
return status & BIT(shift);
}

However I missed the fact that writing uses set and clear registers,
which effectively means that this approach can't really be used for
writing, so I'm okay with keeping this as is.

>
>> > diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
>> > new file mode 100644
>> > index 0000000..a44dcbf
>> > --- /dev/null
>> > +++ b/drivers/clk/mediatek/clk-gate.h
>> > @@ -0,0 +1,49 @@
>> > +/*
>> > + * Copyright (c) 2014 MediaTek Inc.
>> > + * Author: James Liao <[email protected]>
>>
>> Might not be necessary, but maybe the other people (all or some of
>> them) from signed-off-by should be added to this and other copyright
>> statements?
>
> I rather do not want to update these copyrights frequently. Otherwise we
> would see a lot of patches with an extra hunk changing the copyrights.
> I'm glad we left that behind and look at the git history instead.
> The above is the original author. I don't want to remove him, but I also
> do not want to add every other person who touched that file.

Alright. I just wanted to make sure that this is desired state.

>
> The other stuff will be fixed in the next round. Thanks for reviewing.

You're welcome. Looking forward for next revision.

Best regards,
Tomasz

2015-02-16 09:56:54

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH 13/13] mfd: Add support for the MediaTek MT6397 PMIC

> From: Flora Fu <[email protected]>
>
> This adds support for the MediaTek MT6397 PMIC. This is a
> multifunction device with the following sub modules:
>
> - Regulator
> - RTC
> - Audio codec
> - GPIO
> - Clock
>
> It is interfaced to the host controller using SPI interface by a proprietary
> hardware called PMIC wrapper or pwrap. MT6397 MFD is a child device of the
> pwrap.
>
> Signed-off-by: Flora Fu, MediaTek
> Signed-off-by: Sascha Hauer <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> Cc: Lee Jones <[email protected]>
> ---
> drivers/mfd/Kconfig | 10 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/mt6397-core.c | 223 +++++++++++++++++++++
> include/linux/mfd/mt6397/core.h | 64 +++++++
> include/linux/mfd/mt6397/registers.h | 362 +++++++++++++++++++++++++++++++++++
> 5 files changed, 660 insertions(+)
> create mode 100644 drivers/mfd/mt6397-core.c
> create mode 100644 include/linux/mfd/mt6397/core.h
> create mode 100644 include/linux/mfd/mt6397/registers.h

Looks okay to me now.

Acked-by: Lee Jones <[email protected]>

What's the merge plan for this set?

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

2015-02-19 08:24:19

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 06/13] clk: mediatek: Add basic clocks for Mediatek MT8173.

On Fri, Feb 13, 2015 at 06:56:53PM +0900, Tomasz Figa wrote:
> Please find my comments inline.
>
> On Mon, Feb 9, 2015 at 7:47 PM, Sascha Hauer <[email protected]> wrote:
> > From: James Liao <[email protected]>
> >
> > This patch adds basic clocks for MT8173, including TOPCKGEN, PLLs,
> > INFRA and PERI clocks.
> >
> > Signed-off-by: James Liao <[email protected]>
> > Signed-off-by: Henry Chen <[email protected]>
> > Signed-off-by: Sascha Hauer <[email protected]>
> > ---
> > drivers/clk/mediatek/Makefile | 1 +
> > drivers/clk/mediatek/clk-mt8173-pll.c | 807 +++++++++++++++++++++++++
> > drivers/clk/mediatek/clk-mt8173-pll.h | 14 +
> > drivers/clk/mediatek/clk-mt8173.c | 1035 +++++++++++++++++++++++++++++++++
> > 4 files changed, 1857 insertions(+)
> > create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.c
> > create mode 100644 drivers/clk/mediatek/clk-mt8173-pll.h
> > create mode 100644 drivers/clk/mediatek/clk-mt8173.c
> >
> > diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
> > index afb52e5..e030416 100644
> > --- a/drivers/clk/mediatek/Makefile
> > +++ b/drivers/clk/mediatek/Makefile
> > @@ -1,3 +1,4 @@
> > obj-y += clk-mtk.o clk-pll.o clk-gate.o
> > obj-$(CONFIG_RESET_CONTROLLER) += reset.o
> > obj-y += clk-mt8135.o clk-mt8135-pll.o
> > +obj-y += clk-mt8173.o clk-mt8173-pll.o
> > diff --git a/drivers/clk/mediatek/clk-mt8173-pll.c b/drivers/clk/mediatek/clk-mt8173-pll.c
> > new file mode 100644
> > index 0000000..9f6f821
> > --- /dev/null
> > +++ b/drivers/clk/mediatek/clk-mt8173-pll.c
> > @@ -0,0 +1,807 @@
> > +/*
> > + * Copyright (c) 2014 MediaTek Inc.
> > + * Author: James Liao <[email protected]>
> > + *
> > + * 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/io.h>
> > +#include <linux/slab.h>
> > +#include <linux/delay.h>
> > +#include <linux/clkdev.h>
> > +
> > +#include "clk-mtk.h"
> > +#include "clk-pll.h"
> > +#include "clk-mt8173-pll.h"
> > +
> > +#define PLL_BASE_EN BIT(0)
> > +#define PLL_PWR_ON BIT(0)
> > +#define PLL_ISO_EN BIT(1)
> > +#define PLL_PCW_CHG BIT(31)
> > +#define RST_BAR_MASK BIT(24)
> > +#define AUDPLL_TUNER_EN BIT(31)
> > +
> > +static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 };
>
> It might be nice to have a comment what this array is for and how the
> values were calculated.

It's the table for a power of two divider. This can be calculated, no
need for a table.

>
> > +
> > +static u32 mtk_calc_pll_vco_freq(
> > + u32 fin,
> > + u32 pcw,
> > + u32 vcodivsel,
> > + u32 prediv,
> > + u32 pcwfbits)
> > +{
> > + /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */
> > + u64 vco = fin;
> > + u8 c = 0;
> > +
> > + vco = vco * pcw * vcodivsel;
>
> Could you use here (u64)fin directly for increased readability and
> drop the initialization of vco?

yes

>
> > + do_div(vco, prediv);
> > +
> > + if (vco & GENMASK(pcwfbits - 1, 0))
> > + c = 1;
>
> What is c? Could the variable has a more meaningful name?

I have no idea. This is not explained in the datasheet.

>
> > +
> > + vco >>= pcwfbits;
> > +
> > + if (c)
> > + ++vco;
> > +
> > + return (u32)vco;
> > +}
> > +
> > +static u32 mtk_freq_limit(u32 freq)
> > +{
> > + static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
>
> 3 GHz probably? Could you define (if not defined somewhere already) a
> macro for GHZ and write this as 3 * GHZ?

Did that.

>
> > + static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 62.5 MHz */
>
> Why don't you write it as 62500 * KHZ or 62 * MHZ + 500 * KHZ?
>
> > +
> > + if (freq <= freq_min)
> > + freq = freq_min + 16;
>
> Could you explain what's happening here? Where does the 16 come from
> and why it is not defined as a macro?

I don't know what's going on here. What I find suspicious is that when
freq is between freq_min and freq_min + 16 it is not changed. I just
dropped this. Whoever thinks he needs this can probably explain what
it's good for.

>
> > + else if (freq > freq_max)
> > + freq = freq_max;
> > +
> > + return freq;
> > +}
> > +
> > +static int mtk_calc_pll_freq_cfg(
> > + u32 *pcw,
> > + u32 *postdiv_idx,
> > + u32 freq,
> > + u32 fin,
> > + int pcwfbits)
> > +{
> > + static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
> > + static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */
> > + static const u64 postdiv[] = { 1, 2, 4, 8, 16 };
> > + u64 n_info;
> > + u32 idx;
> > +
> > + /* search suitable postdiv */
> > + for (idx = *postdiv_idx;
> > + idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min;
> > + idx++)
> > + ;
>
> Please document the arguments of this function. It is not obvious why
> the value at postdiv_idx is used as starting point, even though this
> pointer is also used to store the output value...

It seems it is used by some callers to ensure a minimum divider.

>
> > +
> > + if (idx >= ARRAY_SIZE(postdiv))
> > + return -EINVAL; /* freq is out of range (too low) */
> > + else if (postdiv[idx] * freq > freq_max)
> > + return -EINVAL; /* freq is out of range (too high) */
> > +
> > + /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */
> > + n_info = (postdiv[idx] * freq) << pcwfbits;
> > + do_div(n_info, fin);
> > +
> > + *postdiv_idx = idx;
> > + *pcw = (u32)n_info;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_clk_pll_is_enabled(struct clk_hw *hw)
> > +{
> > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > +
> > + return (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0;
> > +}
> > +
> > +static int mtk_clk_pll_prepare(struct clk_hw *hw)
>
> Hmm, contents of this function don't seem to sleep. Maybe this should
> be enable instead of prepare?

Hm, I think I rather use usleep_range instead of udelay and keep it in
the prepare/unprepare path. I don't think there's need to enable/disable
the PLLs in the hot pathes.

> > +const struct clk_ops mt8173_arm_pll_ops = {
> > + .is_enabled = mtk_clk_pll_is_enabled,
> > + .prepare = mtk_clk_pll_prepare,
> > + .unprepare = mtk_clk_pll_unprepare,
>
> Uhh, this is incorrect. If you provide prepare+unprepare, you also
> need to provide is_prepared, not is_enabled. However, considering my
> comments above, it should be possible to use enable+disable instead.

I will decide for one of both.

>
> > + .recalc_rate = mtk_clk_arm_pll_recalc_rate,
> > + .round_rate = mtk_clk_pll_round_rate,
> > + .set_rate = mtk_clk_arm_pll_set_rate,
> > +};
> > +
> > +static long mtk_clk_mm_pll_round_rate(
> > + struct clk_hw *hw,
> > + unsigned long rate,
> > + unsigned long *prate)
> > +{
> > + u32 pcwfbits = 14;
> > + u32 pcw = 0;
> > + u32 postdiv = 0;
> > + u32 r;
> > +
> > + if (rate <= 702000000)
> > + postdiv = 2;
> > +
> > + *prate = *prate ? *prate : 26000000;
>
> I feel like it wouldn't really be a bad idea to define all the numeric
> constants as macros.

The above is unnecessary. The clk framework will never call us with
prate == NULL.

> > + /* postdiv */
> > + con0 &= ~UNIV_PLL_POSTDIV_MASK;
> > + con0 |= postdiv_idx << UNIV_PLL_POSTDIV_L;
> > +
> > + /* fkbdiv */
> > + con1 &= ~UNIV_PLL_FBKDIV_MASK;
> > + con1 |= pcw << UNIV_PLL_FBKDIV_L;
> > +
> > + writel_relaxed(con0, con0_addr);
> > + writel_relaxed(con1, con1_addr);
> > +
> > + if (pll_en) {
> > + wmb(); /* sync write before delay */
>
> The comment should say why, not what, because you can easily see that
> from the code (wmb() before udelay(20) obviously can't be anything
> else than "sync write before delay").

I'll drop the comment.

> > + parent_rate = parent_rate ? parent_rate : 26000000;
> > + r = mtk_calc_pll_freq_cfg(&pcw, &postdiv_idx, rate,
> > + parent_rate, pcwfbits);
> > +
> > + if (r == 0)
>
> I wonder if you shouldn't consider adding an error message to opposite case.

I'l refactor this so that mtk_calc_pll_freq_cfg() can't fail. This won't
be necessary anymore.

> > +static int mtk_clk_aud_pll_prepare(struct clk_hw *hw)
> > +{
> > + unsigned long flags = 0;
>
> No need to initialize.
>
> > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > + void __iomem *con0_addr = pll->base_addr;
> > + void __iomem *con2_addr = pll->base_addr + 8;
>
> A macro for the offset would look better.
>
> > + u32 r;
> > +
> > + spin_lock_irqsave(pll->lock, flags);
> > +
> > + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
> > + writel_relaxed(r, pll->pwr_addr);
> > + wmb(); /* sync write before delay */
>
> Why? And couldn't you use writel() instead of writel_relaxed() + wmb()?

The original author claims this is needed. I can't prove the opposite,
so I kept it.

Anyway, it seems that writel() is writel_relaxed() + a wmb(), so I'll
change it.

> > +#include <dt-bindings/clock/mt8173-clk.h>
> > +
> > +/* ROOT */
> > +#define clk_null "clk_null"
> > +#define clk26m "clk26m"
> > +#define clk32k "clk32k"
>
> Hmm, what's this? What's the purpose of defining the same string, just
> without the quotation marks?

I think the intention was to let the compiler detect typos when using
the same strings multiple times. I don't like this either, will drop.

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2015-02-19 21:42:15

by Mike Turquette

[permalink] [raw]
Subject: Re: [PATCH 13/13] mfd: Add support for the MediaTek MT6397 PMIC

Quoting Lee Jones (2015-02-19 04:13:04)
> On Thu, 19 Feb 2015, Sascha Hauer wrote:
>
> > On Thu, Feb 19, 2015 at 08:43:49AM +0000, Lee Jones wrote:
> > > On Thu, 19 Feb 2015, Sascha Hauer wrote:
> > >
> > > > > > Looks okay to me now.
> > > > > >
> > > > > > Acked-by: Lee Jones <[email protected]>
> > > > > >
> > > > > > What's the merge plan for this set?
> > > > >
> > > > > Patches 1-9 are clock related an several of them have review comments
> > > > > that need to be addressed. I wonder if a V2 series can break out the
> > > > > various subsystems bits from each other?
> > > >
> > > > I'll send a new series later this day. These used to be two series, but
> > > > the PMIC wrapper patches depend on the clock and reset controllers, also
> > > > the device nodes depend on the clock/reset defines from the clock
> > > > support patches. What do you suggest? In the early days of a SoC
> > > > everything seems to depend on everything.
> > >
> > > Only build dependencies count. So long as the Kconfigs are setup
> > > correct, there shouldn't be any issue in taking patches in one
> > > subsystem at a time.
> >
> > The dts snippets need the files in include/dt-bindings, so indeed this
> > is a build dependency. However, this comes only in with the dts changes.
> >
> > So here's the plan:
> >
> > - Mike takes the clk patches
> > - Matthias takes the pmic wrapper driver (in drivers/soc/mediatek/)
> > - You take the MT6397 core driver.
>
> Sounds reasonable. Just ensure that each set is orthogonal and builds
> (or doesn't attempt to) and we'll be in a good place.

Agreed.

Regards,
Mike

>
> > I'll queue up the dts changes locally and ask Arnd to take these after
> > next -rc1 so that all dependencies are in. Unfortunately this means that
> > the patches can't be tested until everything is together after next
> > -rc1.
>
> I'm sure you will be diligent enough to test the interoperability of
> the sets combined. Failing that we can deal with any unavoidable
> fall-out during the -rcs.
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog