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