Commit | Line | Data |
---|---|---|
ea4abe7f BB |
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 | } |