1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | /* |
4 | * ACPI support for platform bus type. |
5 | * |
6 | * Copyright (C) 2015, Linaro Ltd |
7 | * Author: Graeme Gregory <graeme.gregory@linaro.org> |
8 | */ |
9 | |
10 | #include <linux/acpi.h> |
11 | #include <linux/amba/bus.h> |
12 | #include <linux/clkdev.h> |
13 | #include <linux/clk-provider.h> |
14 | #include <linux/device.h> |
15 | #include <linux/err.h> |
16 | #include <linux/ioport.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | |
20 | #include "init.h" |
21 | |
22 | static const struct acpi_device_id amba_id_list[] = { |
23 | {"ARMH0061" , 0}, /* PL061 GPIO Device */ |
24 | {"ARMH0330" , 0}, /* ARM DMA Controller DMA-330 */ |
25 | {"ARMHC501" , 0}, /* ARM CoreSight ETR */ |
26 | {"ARMHC502" , 0}, /* ARM CoreSight STM */ |
27 | {"ARMHC503" , 0}, /* ARM CoreSight Debug */ |
28 | {"ARMHC979" , 0}, /* ARM CoreSight TPIU */ |
29 | {"ARMHC97C" , 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */ |
30 | {"ARMHC98D" , 0}, /* ARM CoreSight Dynamic Replicator */ |
31 | {"ARMHC9CA" , 0}, /* ARM CoreSight CATU */ |
32 | {"ARMHC9FF" , 0}, /* ARM CoreSight Dynamic Funnel */ |
33 | {"" , 0}, |
34 | }; |
35 | |
36 | static void amba_register_dummy_clk(void) |
37 | { |
38 | static struct clk *amba_dummy_clk; |
39 | |
40 | /* If clock already registered */ |
41 | if (amba_dummy_clk) |
42 | return; |
43 | |
44 | amba_dummy_clk = clk_register_fixed_rate(NULL, name: "apb_pclk" , NULL, flags: 0, fixed_rate: 0); |
45 | clk_register_clkdev(amba_dummy_clk, "apb_pclk" , NULL); |
46 | } |
47 | |
48 | static int amba_handler_attach(struct acpi_device *adev, |
49 | const struct acpi_device_id *id) |
50 | { |
51 | struct acpi_device *parent = acpi_dev_parent(adev); |
52 | struct amba_device *dev; |
53 | struct resource_entry *rentry; |
54 | struct list_head resource_list; |
55 | bool address_found = false; |
56 | int irq_no = 0; |
57 | int ret; |
58 | |
59 | /* If the ACPI node already has a physical device attached, skip it. */ |
60 | if (adev->physical_node_count) |
61 | return 0; |
62 | |
63 | dev = amba_device_alloc(dev_name(dev: &adev->dev), 0, 0); |
64 | if (!dev) { |
65 | dev_err(&adev->dev, "%s(): amba_device_alloc() failed\n" , |
66 | __func__); |
67 | return -ENOMEM; |
68 | } |
69 | |
70 | INIT_LIST_HEAD(list: &resource_list); |
71 | ret = acpi_dev_get_resources(adev, list: &resource_list, NULL, NULL); |
72 | if (ret < 0) |
73 | goto err_free; |
74 | |
75 | list_for_each_entry(rentry, &resource_list, node) { |
76 | switch (resource_type(res: rentry->res)) { |
77 | case IORESOURCE_MEM: |
78 | if (!address_found) { |
79 | dev->res = *rentry->res; |
80 | dev->res.name = dev_name(dev: &dev->dev); |
81 | address_found = true; |
82 | } |
83 | break; |
84 | case IORESOURCE_IRQ: |
85 | if (irq_no < AMBA_NR_IRQS) |
86 | dev->irq[irq_no++] = rentry->res->start; |
87 | break; |
88 | default: |
89 | dev_warn(&adev->dev, "Invalid resource\n" ); |
90 | break; |
91 | } |
92 | } |
93 | |
94 | acpi_dev_free_resource_list(list: &resource_list); |
95 | |
96 | /* |
97 | * If the ACPI node has a parent and that parent has a physical device |
98 | * attached to it, that physical device should be the parent of |
99 | * the amba device we are about to create. |
100 | */ |
101 | if (parent) |
102 | dev->dev.parent = acpi_get_first_physical_node(adev: parent); |
103 | |
104 | device_set_node(dev: &dev->dev, fwnode: acpi_fwnode_handle(adev)); |
105 | |
106 | ret = amba_device_add(dev, &iomem_resource); |
107 | if (ret) { |
108 | dev_err(&adev->dev, "%s(): amba_device_add() failed (%d)\n" , |
109 | __func__, ret); |
110 | goto err_free; |
111 | } |
112 | |
113 | return 1; |
114 | |
115 | err_free: |
116 | amba_device_put(dev); |
117 | return ret; |
118 | } |
119 | |
120 | static struct acpi_scan_handler amba_handler = { |
121 | .ids = amba_id_list, |
122 | .attach = amba_handler_attach, |
123 | }; |
124 | |
125 | void __init acpi_amba_init(void) |
126 | { |
127 | amba_register_dummy_clk(); |
128 | acpi_scan_add_handler(handler: &amba_handler); |
129 | } |
130 | |