blob: 30a81262ce5da3c767078e7294781f8606e71742 [file] [log] [blame]
Furquan Shaikh20a91c92017-02-11 11:16:18 -08001/*
2 * This file is part of the coreboot project.
3 *
Furquan Shaikh20a91c92017-02-11 11:16:18 -08004 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <arch/acpi_device.h>
15#include <arch/acpigen.h>
16#include <console/console.h>
17#include <device/device.h>
18#include <device/path.h>
19#include <device/spi.h>
20#include <spi-generic.h>
21#include <stdint.h>
22#include <string.h>
23#include "chip.h"
24
Aaron Durbinaa090cb2017-09-13 16:01:52 -060025static int spi_acpi_get_bus(const struct device *dev)
Furquan Shaikh20a91c92017-02-11 11:16:18 -080026{
27 struct device *spi_dev;
28 struct device_operations *ops;
29
30 if (!dev->bus || !dev->bus->dev)
31 return -1;
32
33 spi_dev = dev->bus->dev;
34 ops = spi_dev->ops;
35
36 if (ops && ops->ops_spi_bus &&
37 ops->ops_spi_bus->dev_to_bus)
38 return ops->ops_spi_bus->dev_to_bus(spi_dev);
39
40 return -1;
41}
42
Duncan Lauriec9db3842017-02-17 17:14:35 -080043static bool spi_acpi_add_gpios_to_crs(struct drivers_spi_acpi_config *config)
44{
45 /*
46 * Return false if:
47 * 1. Request to explicitly disable export of GPIOs in CRS, or
48 * 2. Both reset and enable GPIOs are not provided.
49 */
50 if (config->disable_gpio_export_in_crs ||
51 ((config->reset_gpio.pin_count == 0) &&
52 (config->enable_gpio.pin_count == 0)))
53 return false;
54
55 return true;
56}
57
58static int spi_acpi_write_gpio(struct acpi_gpio *gpio, int *curr_index)
59{
60 int ret = -1;
61
62 if (gpio->pin_count == 0)
63 return ret;
64
65 acpi_device_write_gpio(gpio);
66 ret = *curr_index;
67 (*curr_index)++;
68
69 return ret;
70}
71
Furquan Shaikh20a91c92017-02-11 11:16:18 -080072static void spi_acpi_fill_ssdt_generator(struct device *dev)
73{
74 struct drivers_spi_acpi_config *config = dev->chip_info;
75 const char *scope = acpi_device_scope(dev);
Duncan Lauriec9db3842017-02-17 17:14:35 -080076 const char *path = acpi_device_path(dev);
Furquan Shaikh20a91c92017-02-11 11:16:18 -080077 struct acpi_spi spi = {
Furquan Shaikh5bda6422017-04-07 07:13:24 -070078 .device_select = dev->path.spi.cs,
Furquan Shaikh20a91c92017-02-11 11:16:18 -080079 .speed = config->speed ? : 1 * MHz,
80 .resource = scope,
Furquan Shaikh5bda6422017-04-07 07:13:24 -070081 .device_select_polarity = SPI_POLARITY_LOW,
82 .wire_mode = SPI_4_WIRE_MODE,
83 .data_bit_length = 8,
84 .clock_phase = SPI_CLOCK_PHASE_FIRST,
85 .clock_polarity = SPI_POLARITY_LOW,
Furquan Shaikh20a91c92017-02-11 11:16:18 -080086 };
Duncan Lauriec9db3842017-02-17 17:14:35 -080087 int curr_index = 0;
88 int irq_gpio_index = -1;
89 int reset_gpio_index = -1;
90 int enable_gpio_index = -1;
Furquan Shaikh20a91c92017-02-11 11:16:18 -080091
92 if (!dev->enabled || !scope)
93 return;
94
Furquan Shaikh5bda6422017-04-07 07:13:24 -070095 if (spi_acpi_get_bus(dev) == -1) {
Furquan Shaikh20a91c92017-02-11 11:16:18 -080096 printk(BIOS_ERR, "%s: ERROR: Cannot get bus for device.\n",
Furquan Shaikh5bda6422017-04-07 07:13:24 -070097 dev_path(dev));
Furquan Shaikh20a91c92017-02-11 11:16:18 -080098 return;
99 }
100
101 if (!config->hid) {
102 printk(BIOS_ERR, "%s: ERROR: HID required.\n", dev_path(dev));
103 return;
104 }
105
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800106 /* Device */
107 acpigen_write_scope(scope);
108 acpigen_write_device(acpi_device_name(dev));
109 acpigen_write_name_string("_HID", config->hid);
110 if (config->cid)
111 acpigen_write_name_string("_CID", config->cid);
112 acpigen_write_name_integer("_UID", config->uid);
113 if (config->desc)
114 acpigen_write_name_string("_DDN", config->desc);
Hung-Te Linb4be50c2018-09-10 10:55:49 +0800115 acpigen_write_STA(acpi_device_status(dev));
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800116
117 /* Resources */
118 acpigen_write_name("_CRS");
119 acpigen_write_resourcetemplate_header();
120 acpi_device_write_spi(&spi);
Duncan Lauriec9db3842017-02-17 17:14:35 -0800121
122 /* Use either Interrupt() or GpioInt() */
123 if (config->irq_gpio.pin_count)
124 irq_gpio_index = spi_acpi_write_gpio(&config->irq_gpio,
125 &curr_index);
126 else
127 acpi_device_write_interrupt(&config->irq);
128
129 /* Add enable/reset GPIOs if needed */
130 if (spi_acpi_add_gpios_to_crs(config)) {
131 reset_gpio_index = spi_acpi_write_gpio(&config->reset_gpio,
132 &curr_index);
133 enable_gpio_index = spi_acpi_write_gpio(&config->enable_gpio,
134 &curr_index);
135 }
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800136 acpigen_write_resourcetemplate_footer();
137
Duncan Lauriec9db3842017-02-17 17:14:35 -0800138 /* Wake capabilities */
139 if (config->wake) {
140 acpigen_write_name_integer("_S0W", 4);
141 acpigen_write_PRW(config->wake, 3);
142 };
143
144 /* Write device properties if needed */
145 if (config->compat_string || irq_gpio_index >= 0 ||
146 reset_gpio_index >= 0 || enable_gpio_index >= 0) {
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800147 struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
Duncan Lauriec9db3842017-02-17 17:14:35 -0800148 if (config->compat_string)
149 acpi_dp_add_string(dsd, "compatible",
150 config->compat_string);
151 if (irq_gpio_index >= 0)
152 acpi_dp_add_gpio(dsd, "irq-gpios", path,
153 irq_gpio_index, 0,
154 config->irq_gpio.polarity);
155 if (reset_gpio_index >= 0)
156 acpi_dp_add_gpio(dsd, "reset-gpios", path,
157 reset_gpio_index, 0,
158 config->reset_gpio.polarity);
159 if (enable_gpio_index >= 0)
160 acpi_dp_add_gpio(dsd, "enable-gpios", path,
161 enable_gpio_index, 0,
162 config->enable_gpio.polarity);
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800163 acpi_dp_write(dsd);
164 }
165
Duncan Lauriec9db3842017-02-17 17:14:35 -0800166 /* Power Resource */
Shelley Chena0603392018-04-26 13:52:30 -0700167 if (config->has_power_resource) {
168 const struct acpi_power_res_params power_res_params = {
169 &config->reset_gpio,
170 config->reset_delay_ms,
171 config->reset_off_delay_ms,
172 &config->enable_gpio,
173 config->enable_delay_ms,
174 config->enable_off_delay_ms,
175 &config->stop_gpio,
176 config->stop_delay_ms,
177 config->stop_off_delay_ms
178 };
179 acpi_device_add_power_res(&power_res_params);
180 }
Duncan Lauriec9db3842017-02-17 17:14:35 -0800181
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800182 acpigen_pop_len(); /* Device */
183 acpigen_pop_len(); /* Scope */
Duncan Lauriec9db3842017-02-17 17:14:35 -0800184
185 printk(BIOS_INFO, "%s: %s at %s\n", path,
186 config->desc ? : dev->chip_ops->name, dev_path(dev));
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800187}
188
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600189static const char *spi_acpi_name(const struct device *dev)
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800190{
191 struct drivers_spi_acpi_config *config = dev->chip_info;
192 static char name[5];
193
194 if (config->name)
195 return config->name;
196
197 snprintf(name, sizeof(name), "S%03.3X", spi_acpi_get_bus(dev));
198 name[4] = '\0';
199 return name;
200}
201
202static struct device_operations spi_acpi_ops = {
Nico Huber68680dd2020-03-31 17:34:52 +0200203 .read_resources = DEVICE_NOOP,
204 .set_resources = DEVICE_NOOP,
205 .enable_resources = DEVICE_NOOP,
206 .acpi_name = spi_acpi_name,
207 .acpi_fill_ssdt = spi_acpi_fill_ssdt_generator,
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800208};
209
210static void spi_acpi_enable(struct device *dev)
211{
212 dev->ops = &spi_acpi_ops;
213}
214
215struct chip_operations drivers_spi_acpi_ops = {
216 CHIP_NAME("SPI Device")
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100217 .enable_dev = spi_acpi_enable
Furquan Shaikh20a91c92017-02-11 11:16:18 -0800218};