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 |
| 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 | } |