1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/io.h> |
3 | #include <linux/init.h> |
4 | #include <linux/ioport.h> |
5 | #include <linux/export.h> |
6 | #include <linux/of.h> |
7 | #include <linux/platform_device.h> |
8 | |
9 | static unsigned long acpi_iobase; |
10 | |
11 | #define ACPI_PM_EVT_BLK (acpi_iobase + 0x00) /* 4 bytes */ |
12 | #define ACPI_PM_CNT_BLK (acpi_iobase + 0x04) /* 2 bytes */ |
13 | #define ACPI_PMA_CNT_BLK (acpi_iobase + 0x0F) /* 1 byte */ |
14 | #define ACPI_PM_TMR_BLK (acpi_iobase + 0x18) /* 4 bytes */ |
15 | #define ACPI_GPE0_BLK (acpi_iobase + 0x10) /* 8 bytes */ |
16 | #define ACPI_END (acpi_iobase + 0x80) |
17 | |
18 | #define PM_INDEX 0xCD6 |
19 | #define PM_DATA 0xCD7 |
20 | #define PM2_INDEX 0xCD0 |
21 | #define PM2_DATA 0xCD1 |
22 | |
23 | static void pmio_write_index(u16 index, u8 reg, u8 value) |
24 | { |
25 | outb(value: reg, port: index); |
26 | outb(value, port: index + 1); |
27 | } |
28 | |
29 | static u8 pmio_read_index(u16 index, u8 reg) |
30 | { |
31 | outb(value: reg, port: index); |
32 | return inb(port: index + 1); |
33 | } |
34 | |
35 | static void pm_iowrite(u8 reg, u8 value) |
36 | { |
37 | pmio_write_index(PM_INDEX, reg, value); |
38 | } |
39 | |
40 | static u8 pm_ioread(u8 reg) |
41 | { |
42 | return pmio_read_index(PM_INDEX, reg); |
43 | } |
44 | |
45 | static void pm2_iowrite(u8 reg, u8 value) |
46 | { |
47 | pmio_write_index(PM2_INDEX, reg, value); |
48 | } |
49 | |
50 | static u8 pm2_ioread(u8 reg) |
51 | { |
52 | return pmio_read_index(PM2_INDEX, reg); |
53 | } |
54 | |
55 | static void acpi_hw_clear_status(void) |
56 | { |
57 | u16 value; |
58 | |
59 | /* PMStatus: Clear WakeStatus/PwrBtnStatus */ |
60 | value = inw(ACPI_PM_EVT_BLK); |
61 | value |= (1 << 8 | 1 << 15); |
62 | outw(value, ACPI_PM_EVT_BLK); |
63 | |
64 | /* GPEStatus: Clear all generated events */ |
65 | outl(inl(ACPI_GPE0_BLK), ACPI_GPE0_BLK); |
66 | } |
67 | |
68 | static void acpi_registers_setup(void) |
69 | { |
70 | u32 value; |
71 | |
72 | /* PM Status Base */ |
73 | pm_iowrite(reg: 0x20, ACPI_PM_EVT_BLK & 0xff); |
74 | pm_iowrite(reg: 0x21, ACPI_PM_EVT_BLK >> 8); |
75 | |
76 | /* PM Control Base */ |
77 | pm_iowrite(reg: 0x22, ACPI_PM_CNT_BLK & 0xff); |
78 | pm_iowrite(reg: 0x23, ACPI_PM_CNT_BLK >> 8); |
79 | |
80 | /* GPM Base */ |
81 | pm_iowrite(reg: 0x28, ACPI_GPE0_BLK & 0xff); |
82 | pm_iowrite(reg: 0x29, ACPI_GPE0_BLK >> 8); |
83 | |
84 | /* ACPI End */ |
85 | pm_iowrite(reg: 0x2e, ACPI_END & 0xff); |
86 | pm_iowrite(reg: 0x2f, ACPI_END >> 8); |
87 | |
88 | /* IO Decode: When AcpiDecodeEnable set, South-Bridge uses the contents |
89 | * of the PM registers at index 0x20~0x2B to decode ACPI I/O address. */ |
90 | pm_iowrite(reg: 0x0e, value: 1 << 3); |
91 | |
92 | /* SCI_EN set */ |
93 | outw(value: 1, ACPI_PM_CNT_BLK); |
94 | |
95 | /* Enable to generate SCI */ |
96 | pm_iowrite(reg: 0x10, value: pm_ioread(reg: 0x10) | 1); |
97 | |
98 | /* GPM3/GPM9 enable */ |
99 | value = inl(ACPI_GPE0_BLK + 4); |
100 | outl(value: value | (1 << 14) | (1 << 22), ACPI_GPE0_BLK + 4); |
101 | |
102 | /* Set GPM9 as input */ |
103 | pm_iowrite(reg: 0x8d, value: pm_ioread(reg: 0x8d) & (~(1 << 1))); |
104 | |
105 | /* Set GPM9 as non-output */ |
106 | pm_iowrite(reg: 0x94, value: pm_ioread(reg: 0x94) | (1 << 3)); |
107 | |
108 | /* GPM3 config ACPI trigger SCIOUT */ |
109 | pm_iowrite(reg: 0x33, value: pm_ioread(reg: 0x33) & (~(3 << 4))); |
110 | |
111 | /* GPM9 config ACPI trigger SCIOUT */ |
112 | pm_iowrite(reg: 0x3d, value: pm_ioread(reg: 0x3d) & (~(3 << 2))); |
113 | |
114 | /* GPM3 config falling edge trigger */ |
115 | pm_iowrite(reg: 0x37, value: pm_ioread(reg: 0x37) & (~(1 << 6))); |
116 | |
117 | /* No wait for STPGNT# in ACPI Sx state */ |
118 | pm_iowrite(reg: 0x7c, value: pm_ioread(reg: 0x7c) | (1 << 6)); |
119 | |
120 | /* Set GPM3 pull-down enable */ |
121 | value = pm2_ioread(reg: 0xf6); |
122 | value |= ((1 << 7) | (1 << 3)); |
123 | pm2_iowrite(reg: 0xf6, value); |
124 | |
125 | /* Set GPM9 pull-down enable */ |
126 | value = pm2_ioread(reg: 0xf8); |
127 | value |= ((1 << 5) | (1 << 1)); |
128 | pm2_iowrite(reg: 0xf8, value); |
129 | } |
130 | |
131 | static int rs780e_acpi_probe(struct platform_device *pdev) |
132 | { |
133 | struct resource *res; |
134 | |
135 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
136 | if (!res) |
137 | return -ENODEV; |
138 | |
139 | /* SCI interrupt need acpi space, allocate here */ |
140 | if (!request_region(res->start, resource_size(res), "acpi" )) { |
141 | pr_err("RS780E-ACPI: Failed to request IO Region\n" ); |
142 | return -EBUSY; |
143 | } |
144 | |
145 | acpi_iobase = res->start; |
146 | |
147 | acpi_registers_setup(); |
148 | acpi_hw_clear_status(); |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | static const struct of_device_id rs780e_acpi_match[] = { |
154 | { .compatible = "loongson,rs780e-acpi" }, |
155 | {}, |
156 | }; |
157 | |
158 | static struct platform_driver rs780e_acpi_driver = { |
159 | .probe = rs780e_acpi_probe, |
160 | .driver = { |
161 | .name = "RS780E-ACPI" , |
162 | .of_match_table = rs780e_acpi_match, |
163 | }, |
164 | }; |
165 | builtin_platform_driver(rs780e_acpi_driver); |
166 | |