Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 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 |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 25 | #define AT91_DBGU_EXID 0x44 |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 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 |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 31 | |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 32 | static int __init at91_get_cidr_exid_from_dbgu(u32 *cidr, u32 *exid) |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 33 | { |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 34 | struct device_node *np; |
| 35 | void __iomem *regs; |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 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"); |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 41 | if (!np) |
| 42 | return -ENODEV; |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 43 | |
| 44 | regs = of_iomap(np, 0); |
| 45 | of_node_put(np); |
| 46 | |
| 47 | if (!regs) { |
| 48 | pr_warn("Could not map DBGU iomem range"); |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 49 | return -ENXIO; |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 50 | } |
| 51 | |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 52 | *cidr = readl(regs + AT91_DBGU_CIDR); |
| 53 | *exid = readl(regs + AT91_DBGU_EXID); |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 54 | |
| 55 | iounmap(regs); |
| 56 | |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 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 | |
| 69 | regs = of_iomap(np, 0); |
| 70 | of_node_put(np); |
| 71 | |
| 72 | if (!regs) { |
| 73 | pr_warn("Could not map DBGU iomem range"); |
| 74 | return -ENXIO; |
| 75 | } |
| 76 | |
| 77 | *cidr = readl(regs + AT91_CHIPID_CIDR); |
| 78 | *exid = readl(regs + AT91_CHIPID_EXID); |
| 79 | |
| 80 | iounmap(regs); |
| 81 | |
| 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 | |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 107 | for (soc = socs; soc->name; soc++) { |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 108 | if (soc->cidr_match != (cidr & AT91_CIDR_MATCH_MASK)) |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 109 | continue; |
| 110 | |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 111 | if (!(cidr & AT91_CIDR_EXT) || soc->exid_match == exid) |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 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", |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 127 | AT91_CIDR_VERSION(cidr)); |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 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, |
Ludovic Desroches | 2f26e61 | 2016-03-18 08:21:19 +0100 | [diff] [blame] | 139 | AT91_CIDR_VERSION(cidr)); |
Boris BREZILLON | ea4abe7 | 2015-03-12 15:54:27 +0100 | [diff] [blame] | 140 | |
| 141 | return soc_dev; |
| 142 | } |