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