1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fb127b79SStefan Agner /*
3fb127b79SStefan Agner * Copyright 2015 Toradex AG
4fb127b79SStefan Agner *
5fb127b79SStefan Agner * Stefan Agner <stefan@agner.ch>
6fb127b79SStefan Agner *
7fb127b79SStefan Agner * Freescale TCON device driver
8fb127b79SStefan Agner */
9fb127b79SStefan Agner
10fb127b79SStefan Agner #include <linux/clk.h>
11fb127b79SStefan Agner #include <linux/io.h>
12fb127b79SStefan Agner #include <linux/mm.h>
13fb127b79SStefan Agner #include <linux/of_address.h>
14fb127b79SStefan Agner #include <linux/platform_device.h>
15fb127b79SStefan Agner #include <linux/regmap.h>
16fb127b79SStefan Agner
17fb127b79SStefan Agner #include "fsl_tcon.h"
18fb127b79SStefan Agner
fsl_tcon_bypass_disable(struct fsl_tcon * tcon)19fb127b79SStefan Agner void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
20fb127b79SStefan Agner {
21fb127b79SStefan Agner regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
22fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS, 0);
23fb127b79SStefan Agner }
24fb127b79SStefan Agner
fsl_tcon_bypass_enable(struct fsl_tcon * tcon)25fb127b79SStefan Agner void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
26fb127b79SStefan Agner {
27fb127b79SStefan Agner regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
28fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS,
29fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS);
30fb127b79SStefan Agner }
31fb127b79SStefan Agner
32fb127b79SStefan Agner static struct regmap_config fsl_tcon_regmap_config = {
33fb127b79SStefan Agner .reg_bits = 32,
34fb127b79SStefan Agner .reg_stride = 4,
35fb127b79SStefan Agner .val_bits = 32,
36fb127b79SStefan Agner
37fb127b79SStefan Agner .name = "tcon",
38fb127b79SStefan Agner };
39fb127b79SStefan Agner
fsl_tcon_init_regmap(struct device * dev,struct fsl_tcon * tcon,struct device_node * np)40fb127b79SStefan Agner static int fsl_tcon_init_regmap(struct device *dev,
41fb127b79SStefan Agner struct fsl_tcon *tcon,
42fb127b79SStefan Agner struct device_node *np)
43fb127b79SStefan Agner {
44fb127b79SStefan Agner struct resource res;
45fb127b79SStefan Agner void __iomem *regs;
46fb127b79SStefan Agner
47fb127b79SStefan Agner if (of_address_to_resource(np, 0, &res))
48fb127b79SStefan Agner return -EINVAL;
49fb127b79SStefan Agner
50fb127b79SStefan Agner regs = devm_ioremap_resource(dev, &res);
51fb127b79SStefan Agner if (IS_ERR(regs))
52fb127b79SStefan Agner return PTR_ERR(regs);
53fb127b79SStefan Agner
54fb127b79SStefan Agner tcon->regs = devm_regmap_init_mmio(dev, regs,
55fb127b79SStefan Agner &fsl_tcon_regmap_config);
56acd4d615SWei Yongjun return PTR_ERR_OR_ZERO(tcon->regs);
57fb127b79SStefan Agner }
58fb127b79SStefan Agner
fsl_tcon_init(struct device * dev)59fb127b79SStefan Agner struct fsl_tcon *fsl_tcon_init(struct device *dev)
60fb127b79SStefan Agner {
61fb127b79SStefan Agner struct fsl_tcon *tcon;
62fb127b79SStefan Agner struct device_node *np;
63fb127b79SStefan Agner int ret;
64fb127b79SStefan Agner
65fb127b79SStefan Agner /* TCON node is not mandatory, some devices do not provide TCON */
66fb127b79SStefan Agner np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
67fb127b79SStefan Agner if (!np)
68fb127b79SStefan Agner return NULL;
69fb127b79SStefan Agner
70fb127b79SStefan Agner tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
715d2883d5SFabio Estevam if (!tcon)
72fb127b79SStefan Agner goto err_node_put;
73fb127b79SStefan Agner
74fb127b79SStefan Agner ret = fsl_tcon_init_regmap(dev, tcon, np);
75fb127b79SStefan Agner if (ret) {
76fb127b79SStefan Agner dev_err(dev, "Couldn't create the TCON regmap\n");
77fb127b79SStefan Agner goto err_node_put;
78fb127b79SStefan Agner }
79fb127b79SStefan Agner
80fb127b79SStefan Agner tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
81fb127b79SStefan Agner if (IS_ERR(tcon->ipg_clk)) {
82fb127b79SStefan Agner dev_err(dev, "Couldn't get the TCON bus clock\n");
83fb127b79SStefan Agner goto err_node_put;
84fb127b79SStefan Agner }
85fb127b79SStefan Agner
86ef15d361SFabio Estevam ret = clk_prepare_enable(tcon->ipg_clk);
87ef15d361SFabio Estevam if (ret) {
88ef15d361SFabio Estevam dev_err(dev, "Couldn't enable the TCON clock\n");
89ef15d361SFabio Estevam goto err_node_put;
90ef15d361SFabio Estevam }
91fb127b79SStefan Agner
92ef15d361SFabio Estevam of_node_put(np);
93fb127b79SStefan Agner dev_info(dev, "Using TCON in bypass mode\n");
94fb127b79SStefan Agner
95fb127b79SStefan Agner return tcon;
96fb127b79SStefan Agner
97fb127b79SStefan Agner err_node_put:
98fb127b79SStefan Agner of_node_put(np);
99fb127b79SStefan Agner return NULL;
100fb127b79SStefan Agner }
101fb127b79SStefan Agner
fsl_tcon_free(struct fsl_tcon * tcon)102fb127b79SStefan Agner void fsl_tcon_free(struct fsl_tcon *tcon)
103fb127b79SStefan Agner {
104fb127b79SStefan Agner clk_disable_unprepare(tcon->ipg_clk);
105fb127b79SStefan Agner clk_put(tcon->ipg_clk);
106fb127b79SStefan Agner }
107fb127b79SStefan Agner
108