diff options
Diffstat (limited to 'arch/arm/mach-at91/soc.c')
-rw-r--r-- | arch/arm/mach-at91/soc.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/soc.c b/arch/arm/mach-at91/soc.c new file mode 100644 index 000000000000..54343ffa3e53 --- /dev/null +++ b/arch/arm/mach-at91/soc.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Atmel | ||
3 | * | ||
4 | * Alexandre Belloni <alexandre.belloni@free-electrons.com | ||
5 | * Boris Brezillon <boris.brezillon@free-electrons.com | ||
6 | * | ||
7 | * This file is licensed under the terms of the GNU General Public | ||
8 | * License version 2. This program is licensed "as is" without any | ||
9 | * warranty of any kind, whether express or implied. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) "AT91: " fmt | ||
14 | |||
15 | #include <linux/io.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/of_address.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/sys_soc.h> | ||
21 | |||
22 | #include "soc.h" | ||
23 | |||
24 | #define AT91_DBGU_CIDR 0x40 | ||
25 | #define AT91_DBGU_CIDR_VERSION(x) ((x) & 0x1f) | ||
26 | #define AT91_DBGU_CIDR_EXT BIT(31) | ||
27 | #define AT91_DBGU_CIDR_MATCH_MASK 0x7fffffe0 | ||
28 | #define AT91_DBGU_EXID 0x44 | ||
29 | |||
30 | struct soc_device * __init at91_soc_init(const struct at91_soc *socs) | ||
31 | { | ||
32 | struct soc_device_attribute *soc_dev_attr; | ||
33 | const struct at91_soc *soc; | ||
34 | struct soc_device *soc_dev; | ||
35 | struct device_node *np; | ||
36 | void __iomem *regs; | ||
37 | u32 cidr, exid; | ||
38 | |||
39 | np = of_find_compatible_node(NULL, NULL, "atmel,at91rm9200-dbgu"); | ||
40 | if (!np) | ||
41 | np = of_find_compatible_node(NULL, NULL, | ||
42 | "atmel,at91sam9260-dbgu"); | ||
43 | |||
44 | if (!np) { | ||
45 | pr_warn("Could not find DBGU node"); | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | regs = of_iomap(np, 0); | ||
50 | of_node_put(np); | ||
51 | |||
52 | if (!regs) { | ||
53 | pr_warn("Could not map DBGU iomem range"); | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | cidr = readl(regs + AT91_DBGU_CIDR); | ||
58 | exid = readl(regs + AT91_DBGU_EXID); | ||
59 | |||
60 | iounmap(regs); | ||
61 | |||
62 | for (soc = socs; soc->name; soc++) { | ||
63 | if (soc->cidr_match != (cidr & AT91_DBGU_CIDR_MATCH_MASK)) | ||
64 | continue; | ||
65 | |||
66 | if (!(cidr & AT91_DBGU_CIDR_EXT) || soc->exid_match == exid) | ||
67 | break; | ||
68 | } | ||
69 | |||
70 | if (!soc->name) { | ||
71 | pr_warn("Could not find matching SoC description\n"); | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | ||
76 | if (!soc_dev_attr) | ||
77 | return NULL; | ||
78 | |||
79 | soc_dev_attr->family = soc->family; | ||
80 | soc_dev_attr->soc_id = soc->name; | ||
81 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", | ||
82 | AT91_DBGU_CIDR_VERSION(cidr)); | ||
83 | soc_dev = soc_device_register(soc_dev_attr); | ||
84 | if (IS_ERR(soc_dev)) { | ||
85 | kfree(soc_dev_attr->revision); | ||
86 | kfree(soc_dev_attr); | ||
87 | pr_warn("Could not register SoC device\n"); | ||
88 | return NULL; | ||
89 | } | ||
90 | |||
91 | if (soc->family) | ||
92 | pr_info("Detected SoC family: %s\n", soc->family); | ||
93 | pr_info("Detected SoC: %s, revision %X\n", soc->name, | ||
94 | AT91_DBGU_CIDR_VERSION(cidr)); | ||
95 | |||
96 | return soc_dev; | ||
97 | } | ||