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 | |
ea4abe7f | 25 | #define AT91_DBGU_EXID 0x44 |
2f26e618 LD |
26 | #define AT91_CHIPID_CIDR 0x00 |
27 | #define AT91_CHIPID_EXID 0x04 | |
28 | #define AT91_CIDR_VERSION(x) ((x) & 0x1f) | |
29 | #define AT91_CIDR_EXT BIT(31) | |
30 | #define AT91_CIDR_MATCH_MASK 0x7fffffe0 | |
ea4abe7f | 31 | |
2f26e618 | 32 | static int __init at91_get_cidr_exid_from_dbgu(u32 *cidr, u32 *exid) |
ea4abe7f | 33 | { |
ea4abe7f BB |
34 | struct device_node *np; |
35 | void __iomem *regs; | |
ea4abe7f BB |
36 | |
37 | np = of_find_compatible_node(NULL, NULL, "atmel,at91rm9200-dbgu"); | |
38 | if (!np) | |
39 | np = of_find_compatible_node(NULL, NULL, | |
40 | "atmel,at91sam9260-dbgu"); | |
2f26e618 LD |
41 | if (!np) |
42 | return -ENODEV; | |
ea4abe7f | 43 | |
2f26e618 LD |
44 | regs = of_iomap(np, 0); |
45 | of_node_put(np); | |
46 | ||
47 | if (!regs) { | |
48 | pr_warn("Could not map DBGU iomem range"); | |
49 | return -ENXIO; | |
ea4abe7f BB |
50 | } |
51 | ||
2f26e618 LD |
52 | *cidr = readl(regs + AT91_DBGU_CIDR); |
53 | *exid = readl(regs + AT91_DBGU_EXID); | |
54 | ||
55 | iounmap(regs); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static int __init at91_get_cidr_exid_from_chipid(u32 *cidr, u32 *exid) | |
61 | { | |
62 | struct device_node *np; | |
63 | void __iomem *regs; | |
64 | ||
65 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-chipid"); | |
66 | if (!np) | |
67 | return -ENODEV; | |
68 | ||
ea4abe7f BB |
69 | regs = of_iomap(np, 0); |
70 | of_node_put(np); | |
71 | ||
72 | if (!regs) { | |
73 | pr_warn("Could not map DBGU iomem range"); | |
2f26e618 | 74 | return -ENXIO; |
ea4abe7f BB |
75 | } |
76 | ||
2f26e618 LD |
77 | *cidr = readl(regs + AT91_CHIPID_CIDR); |
78 | *exid = readl(regs + AT91_CHIPID_EXID); | |
ea4abe7f BB |
79 | |
80 | iounmap(regs); | |
81 | ||
2f26e618 LD |
82 | return 0; |
83 | } | |
84 | ||
85 | struct soc_device * __init at91_soc_init(const struct at91_soc *socs) | |
86 | { | |
87 | struct soc_device_attribute *soc_dev_attr; | |
88 | const struct at91_soc *soc; | |
89 | struct soc_device *soc_dev; | |
90 | u32 cidr, exid; | |
91 | int ret; | |
92 | ||
93 | /* | |
94 | * With SAMA5D2 and later SoCs, CIDR and EXID registers are no more | |
95 | * in the dbgu device but in the chipid device whose purpose is only | |
96 | * to expose these two registers. | |
97 | */ | |
98 | ret = at91_get_cidr_exid_from_dbgu(&cidr, &exid); | |
99 | if (ret) | |
100 | ret = at91_get_cidr_exid_from_chipid(&cidr, &exid); | |
101 | if (ret) { | |
102 | if (ret == -ENODEV) | |
103 | pr_warn("Could not find identification node"); | |
104 | return NULL; | |
105 | } | |
106 | ||
ea4abe7f | 107 | for (soc = socs; soc->name; soc++) { |
2f26e618 | 108 | if (soc->cidr_match != (cidr & AT91_CIDR_MATCH_MASK)) |
ea4abe7f BB |
109 | continue; |
110 | ||
2f26e618 | 111 | if (!(cidr & AT91_CIDR_EXT) || soc->exid_match == exid) |
ea4abe7f BB |
112 | break; |
113 | } | |
114 | ||
115 | if (!soc->name) { | |
116 | pr_warn("Could not find matching SoC description\n"); | |
117 | return NULL; | |
118 | } | |
119 | ||
120 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | |
121 | if (!soc_dev_attr) | |
122 | return NULL; | |
123 | ||
124 | soc_dev_attr->family = soc->family; | |
125 | soc_dev_attr->soc_id = soc->name; | |
126 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", | |
2f26e618 | 127 | AT91_CIDR_VERSION(cidr)); |
ea4abe7f BB |
128 | soc_dev = soc_device_register(soc_dev_attr); |
129 | if (IS_ERR(soc_dev)) { | |
130 | kfree(soc_dev_attr->revision); | |
131 | kfree(soc_dev_attr); | |
132 | pr_warn("Could not register SoC device\n"); | |
133 | return NULL; | |
134 | } | |
135 | ||
136 | if (soc->family) | |
137 | pr_info("Detected SoC family: %s\n", soc->family); | |
138 | pr_info("Detected SoC: %s, revision %X\n", soc->name, | |
2f26e618 | 139 | AT91_CIDR_VERSION(cidr)); |
ea4abe7f BB |
140 | |
141 | return soc_dev; | |
142 | } |