blob: 7685415d942bdf2b265e8e5e14bf0b61a62d922d [file] [log] [blame]
Aamir Bohra01d75f42017-03-30 20:12:21 +05301/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2017 Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
Furquan Shaikha8198eb2017-08-04 16:12:19 -070015
16#include <arch/acpi.h>
Aamir Bohra83f7bae2017-04-26 19:30:41 +053017#include <device/device.h>
18#include <device/pci.h>
Aamir Bohra01d75f42017-03-30 20:12:21 +053019#include <device/pci_def.h>
Aamir Bohra83f7bae2017-04-26 19:30:41 +053020#include <device/pci_ids.h>
Furquan Shaikha8198eb2017-08-04 16:12:19 -070021#include <device/pci_ops.h>
Aamir Bohra01d75f42017-03-30 20:12:21 +053022#include <intelblocks/lpss.h>
23#include <intelblocks/uart.h>
24
Furquan Shaikha8198eb2017-08-04 16:12:19 -070025#define UART_PCI_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)
26
Furquan Shaikh3406dd62017-08-04 15:58:26 -070027static void uart_lpss_init(uintptr_t baseaddr)
28{
29 /* Take UART out of reset */
30 lpss_reset_release(baseaddr);
31
32 /* Set M and N divisor inputs and enable clock */
33 lpss_clk_update(baseaddr, CONFIG_SOC_INTEL_COMMON_LPSS_UART_CLK_M_VAL,
34 CONFIG_SOC_INTEL_COMMON_LPSS_UART_CLK_N_VAL);
35}
36
37void uart_common_init(device_t dev, uintptr_t baseaddr)
Aamir Bohra01d75f42017-03-30 20:12:21 +053038{
39 /* Set UART base address */
40 pci_write_config32(dev, PCI_BASE_ADDRESS_0, baseaddr);
41
42 /* Enable memory access and bus master */
Furquan Shaikha8198eb2017-08-04 16:12:19 -070043 pci_write_config32(dev, PCI_COMMAND, UART_PCI_ENABLE);
Aamir Bohra01d75f42017-03-30 20:12:21 +053044
Furquan Shaikh3406dd62017-08-04 15:58:26 -070045 uart_lpss_init(baseaddr);
Furquan Shaikha8198eb2017-08-04 16:12:19 -070046}
Aamir Bohra01d75f42017-03-30 20:12:21 +053047
Furquan Shaikha8198eb2017-08-04 16:12:19 -070048__attribute__((weak)) device_t pch_uart_get_debug_controller(void)
49{
50 /*
51 * device_t can either be a pointer to struct device (e.g. ramstage) or
52 * a simple integer (e.g. SMM) depending upon whether __SIMPLE_DEVICE__
53 * is defined for the stage. Thus, the return requires additional
54 * casting to uintptr_t.
55 */
56 return (device_t)(uintptr_t)NULL;
57}
58
59bool uart_debug_controller_is_initialized(void)
60{
61 device_t dev;
62 uintptr_t base;
63
64 dev = pch_uart_get_debug_controller();
65 if (!dev)
66 return false;
67
68 base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF;
69 if (!base)
70 return false;
71
72 if ((pci_read_config32(dev, PCI_COMMAND) & UART_PCI_ENABLE)
73 != UART_PCI_ENABLE)
74 return false;
75
76 return !lpss_is_controller_in_reset(base);
Aamir Bohra01d75f42017-03-30 20:12:21 +053077}
Aamir Bohra83f7bae2017-04-26 19:30:41 +053078
79#if ENV_RAMSTAGE
80
81__attribute__((weak)) void pch_uart_read_resources(struct device *dev)
82{
83 pci_dev_read_resources(dev);
84}
85
Furquan Shaikha8198eb2017-08-04 16:12:19 -070086__attribute__((weak)) bool pch_uart_init_debug_controller_on_resume(void)
87{
88 /* By default, do not initialize controller. */
89 return false;
90}
91
92bool uart_is_debug_controller(struct device *dev)
93{
94 return dev == pch_uart_get_debug_controller();
95}
96
97/*
98 * This is a workaround to enable UART controller for the debug port if:
99 * 1. CONSOLE_SERIAL is not enabled in coreboot, and
100 * 2. This boot is S3 resume, and
101 * 3. SoC wants to initialize debug UART controller.
102 *
103 * This workaround is required because Linux kernel hangs on resume if console
104 * is not enabled in coreboot, but it is enabled in kernel and not suspended.
105 */
106static bool uart_controller_needs_init(struct device *dev)
107{
108 /*
109 * If coreboot has CONSOLE_SERIAL enabled, the skip re-initalizing
110 * controller here.
111 */
112 if (IS_ENABLED(CONFIG_CONSOLE_SERIAL))
113 return false;
114
115 /* If this device does not correspond to debug port, then skip. */
116 if (!uart_is_debug_controller(dev))
117 return false;
118
119 /* Initialize UART controller only on S3 resume. */
120 if (!acpi_is_wakeup_s3())
121 return false;
122
123 /*
124 * Call SoC specific routine to confirm it wants to initialize
125 * controller.
126 */
127 return pch_uart_init_debug_controller_on_resume();
128}
129
130static void uart_common_enable_resources(struct device *dev)
131{
132 pci_dev_enable_resources(dev);
133
134 if (uart_controller_needs_init(dev)) {
135 uintptr_t base;
136
137 base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF;
138 if (base)
139 uart_lpss_init(base);
140 }
141}
142
Aamir Bohra83f7bae2017-04-26 19:30:41 +0530143static struct device_operations device_ops = {
144 .read_resources = &pch_uart_read_resources,
145 .set_resources = &pci_dev_set_resources,
Furquan Shaikha8198eb2017-08-04 16:12:19 -0700146 .enable_resources = &uart_common_enable_resources,
Aamir Bohra83f7bae2017-04-26 19:30:41 +0530147};
148
149static const unsigned short pci_device_ids[] = {
150 PCI_DEVICE_ID_INTEL_SPT_UART0,
151 PCI_DEVICE_ID_INTEL_SPT_UART1,
152 PCI_DEVICE_ID_INTEL_SPT_UART2,
153 PCI_DEVICE_ID_INTEL_KBP_H_UART0,
154 PCI_DEVICE_ID_INTEL_KBP_H_UART1,
155 PCI_DEVICE_ID_INTEL_KBP_H_UART2,
156 PCI_DEVICE_ID_INTEL_APL_UART0,
157 PCI_DEVICE_ID_INTEL_APL_UART1,
158 PCI_DEVICE_ID_INTEL_APL_UART2,
159 PCI_DEVICE_ID_INTEL_APL_UART3,
Lijian Zhaobbedef92017-07-29 16:38:38 -0700160 PCI_DEVICE_ID_INTEL_CNL_UART0,
161 PCI_DEVICE_ID_INTEL_CNL_UART1,
162 PCI_DEVICE_ID_INTEL_CNL_UART2,
Hannah Williamsf7149652017-05-13 16:18:02 -0700163 PCI_DEVICE_ID_INTEL_GLK_UART0,
164 PCI_DEVICE_ID_INTEL_GLK_UART1,
165 PCI_DEVICE_ID_INTEL_GLK_UART2,
166 PCI_DEVICE_ID_INTEL_GLK_UART3,
167 0,
Aamir Bohra83f7bae2017-04-26 19:30:41 +0530168};
169
170static const struct pci_driver pch_uart __pci_driver = {
171 .ops = &device_ops,
172 .vendor = PCI_VENDOR_ID_INTEL,
173 .devices = pci_device_ids,
174};
175#endif /* ENV_RAMSTAGE */