diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2014-10-06 09:48:43 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-11-25 11:18:43 -0500 |
commit | 2c86e9fb7263dbca2c21a086090d32ba90129f7b (patch) | |
tree | 7c3cfdadde1aa4f5b02ae953467c78035d5e7ec5 /drivers/mfd/atmel-hlcdc.c | |
parent | 6e3f62f0793ebff3f91076490ff0fbb107939701 (diff) |
mfd: Add atmel-hlcdc driver
The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip
The MFD device provides a regmap and several clocks (those connected
to this hardware block) to its subdevices.
This way concurrent accesses to the iomem range are handled by the regmap
framework, and each subdevice can safely access HLCDC registers.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Tested-by: Anthony Harivel <anthony.harivel@emtrion.de>
Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd/atmel-hlcdc.c')
-rw-r--r-- | drivers/mfd/atmel-hlcdc.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c new file mode 100644 index 000000000000..cfd58f4cc5c3 --- /dev/null +++ b/drivers/mfd/atmel-hlcdc.c | |||
@@ -0,0 +1,122 @@ | |||
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/mfd/atmel-hlcdc.h> | ||
22 | #include <linux/mfd/core.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/regmap.h> | ||
26 | |||
27 | #define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) | ||
28 | |||
29 | static const struct mfd_cell atmel_hlcdc_cells[] = { | ||
30 | { | ||
31 | .name = "atmel-hlcdc-pwm", | ||
32 | .of_compatible = "atmel,hlcdc-pwm", | ||
33 | }, | ||
34 | { | ||
35 | .name = "atmel-hlcdc-dc", | ||
36 | .of_compatible = "atmel,hlcdc-display-controller", | ||
37 | }, | ||
38 | }; | ||
39 | |||
40 | static const struct regmap_config atmel_hlcdc_regmap_config = { | ||
41 | .reg_bits = 32, | ||
42 | .val_bits = 32, | ||
43 | .reg_stride = 4, | ||
44 | .max_register = ATMEL_HLCDC_REG_MAX, | ||
45 | }; | ||
46 | |||
47 | static int atmel_hlcdc_probe(struct platform_device *pdev) | ||
48 | { | ||
49 | struct device *dev = &pdev->dev; | ||
50 | struct atmel_hlcdc *hlcdc; | ||
51 | struct resource *res; | ||
52 | void __iomem *regs; | ||
53 | |||
54 | hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); | ||
55 | if (!hlcdc) | ||
56 | return -ENOMEM; | ||
57 | |||
58 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
59 | regs = devm_ioremap_resource(dev, res); | ||
60 | if (IS_ERR(regs)) | ||
61 | return PTR_ERR(regs); | ||
62 | |||
63 | hlcdc->irq = platform_get_irq(pdev, 0); | ||
64 | if (hlcdc->irq < 0) | ||
65 | return hlcdc->irq; | ||
66 | |||
67 | hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); | ||
68 | if (IS_ERR(hlcdc->periph_clk)) { | ||
69 | dev_err(dev, "failed to get peripheral clock\n"); | ||
70 | return PTR_ERR(hlcdc->periph_clk); | ||
71 | } | ||
72 | |||
73 | hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); | ||
74 | if (IS_ERR(hlcdc->sys_clk)) { | ||
75 | dev_err(dev, "failed to get system clock\n"); | ||
76 | return PTR_ERR(hlcdc->sys_clk); | ||
77 | } | ||
78 | |||
79 | hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); | ||
80 | if (IS_ERR(hlcdc->slow_clk)) { | ||
81 | dev_err(dev, "failed to get slow clock\n"); | ||
82 | return PTR_ERR(hlcdc->slow_clk); | ||
83 | } | ||
84 | |||
85 | hlcdc->regmap = devm_regmap_init_mmio(dev, regs, | ||
86 | &atmel_hlcdc_regmap_config); | ||
87 | if (IS_ERR(hlcdc->regmap)) | ||
88 | return PTR_ERR(hlcdc->regmap); | ||
89 | |||
90 | dev_set_drvdata(dev, hlcdc); | ||
91 | |||
92 | return mfd_add_devices(dev, -1, atmel_hlcdc_cells, | ||
93 | ARRAY_SIZE(atmel_hlcdc_cells), | ||
94 | NULL, 0, NULL); | ||
95 | } | ||
96 | |||
97 | static int atmel_hlcdc_remove(struct platform_device *pdev) | ||
98 | { | ||
99 | mfd_remove_devices(&pdev->dev); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static const struct of_device_id atmel_hlcdc_match[] = { | ||
105 | { .compatible = "atmel,sama5d3-hlcdc" }, | ||
106 | { /* sentinel */ }, | ||
107 | }; | ||
108 | |||
109 | static struct platform_driver atmel_hlcdc_driver = { | ||
110 | .probe = atmel_hlcdc_probe, | ||
111 | .remove = atmel_hlcdc_remove, | ||
112 | .driver = { | ||
113 | .name = "atmel-hlcdc", | ||
114 | .of_match_table = atmel_hlcdc_match, | ||
115 | }, | ||
116 | }; | ||
117 | module_platform_driver(atmel_hlcdc_driver); | ||
118 | |||
119 | MODULE_ALIAS("platform:atmel-hlcdc"); | ||
120 | MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); | ||
121 | MODULE_DESCRIPTION("Atmel HLCDC driver"); | ||
122 | MODULE_LICENSE("GPL v2"); | ||