xref: /openbmc/linux/drivers/mfd/atmel-hlcdc.c (revision e2c75e76)
1 /*
2  * Copyright (C) 2014 Free Electrons
3  * Copyright (C) 2014 Atmel
4  *
5  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <linux/clk.h>
21 #include <linux/iopoll.h>
22 #include <linux/mfd/atmel-hlcdc.h>
23 #include <linux/mfd/core.h>
24 #include <linux/module.h>
25 #include <linux/platform_device.h>
26 #include <linux/regmap.h>
27 
28 #define ATMEL_HLCDC_REG_MAX		(0x4000 - 0x4)
29 
30 struct atmel_hlcdc_regmap {
31 	void __iomem *regs;
32 };
33 
34 static const struct mfd_cell atmel_hlcdc_cells[] = {
35 	{
36 		.name = "atmel-hlcdc-pwm",
37 		.of_compatible = "atmel,hlcdc-pwm",
38 	},
39 	{
40 		.name = "atmel-hlcdc-dc",
41 		.of_compatible = "atmel,hlcdc-display-controller",
42 	},
43 };
44 
45 static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
46 					unsigned int val)
47 {
48 	struct atmel_hlcdc_regmap *hregmap = context;
49 
50 	if (reg <= ATMEL_HLCDC_DIS) {
51 		u32 status;
52 
53 		readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
54 					  status, !(status & ATMEL_HLCDC_SIP),
55 					  1, 100);
56 	}
57 
58 	writel(val, hregmap->regs + reg);
59 
60 	return 0;
61 }
62 
63 static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg,
64 				       unsigned int *val)
65 {
66 	struct atmel_hlcdc_regmap *hregmap = context;
67 
68 	*val = readl(hregmap->regs + reg);
69 
70 	return 0;
71 }
72 
73 static const struct regmap_config atmel_hlcdc_regmap_config = {
74 	.reg_bits = 32,
75 	.val_bits = 32,
76 	.reg_stride = 4,
77 	.max_register = ATMEL_HLCDC_REG_MAX,
78 	.reg_write = regmap_atmel_hlcdc_reg_write,
79 	.reg_read = regmap_atmel_hlcdc_reg_read,
80 	.fast_io = true,
81 };
82 
83 static int atmel_hlcdc_probe(struct platform_device *pdev)
84 {
85 	struct atmel_hlcdc_regmap *hregmap;
86 	struct device *dev = &pdev->dev;
87 	struct atmel_hlcdc *hlcdc;
88 	struct resource *res;
89 
90 	hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL);
91 	if (!hregmap)
92 		return -ENOMEM;
93 
94 	hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
95 	if (!hlcdc)
96 		return -ENOMEM;
97 
98 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
99 	hregmap->regs = devm_ioremap_resource(dev, res);
100 	if (IS_ERR(hregmap->regs))
101 		return PTR_ERR(hregmap->regs);
102 
103 	hlcdc->irq = platform_get_irq(pdev, 0);
104 	if (hlcdc->irq < 0)
105 		return hlcdc->irq;
106 
107 	hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
108 	if (IS_ERR(hlcdc->periph_clk)) {
109 		dev_err(dev, "failed to get peripheral clock\n");
110 		return PTR_ERR(hlcdc->periph_clk);
111 	}
112 
113 	hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
114 	if (IS_ERR(hlcdc->sys_clk)) {
115 		dev_err(dev, "failed to get system clock\n");
116 		return PTR_ERR(hlcdc->sys_clk);
117 	}
118 
119 	hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
120 	if (IS_ERR(hlcdc->slow_clk)) {
121 		dev_err(dev, "failed to get slow clock\n");
122 		return PTR_ERR(hlcdc->slow_clk);
123 	}
124 
125 	hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap,
126 					 &atmel_hlcdc_regmap_config);
127 	if (IS_ERR(hlcdc->regmap))
128 		return PTR_ERR(hlcdc->regmap);
129 
130 	dev_set_drvdata(dev, hlcdc);
131 
132 	return devm_mfd_add_devices(dev, -1, atmel_hlcdc_cells,
133 				    ARRAY_SIZE(atmel_hlcdc_cells),
134 				    NULL, 0, NULL);
135 }
136 
137 static const struct of_device_id atmel_hlcdc_match[] = {
138 	{ .compatible = "atmel,at91sam9n12-hlcdc" },
139 	{ .compatible = "atmel,at91sam9x5-hlcdc" },
140 	{ .compatible = "atmel,sama5d2-hlcdc" },
141 	{ .compatible = "atmel,sama5d3-hlcdc" },
142 	{ .compatible = "atmel,sama5d4-hlcdc" },
143 	{ /* sentinel */ },
144 };
145 MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
146 
147 static struct platform_driver atmel_hlcdc_driver = {
148 	.probe = atmel_hlcdc_probe,
149 	.driver = {
150 		.name = "atmel-hlcdc",
151 		.of_match_table = atmel_hlcdc_match,
152 	},
153 };
154 module_platform_driver(atmel_hlcdc_driver);
155 
156 MODULE_ALIAS("platform:atmel-hlcdc");
157 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
158 MODULE_DESCRIPTION("Atmel HLCDC driver");
159 MODULE_LICENSE("GPL v2");
160