blob: 6debe88eeb8d44fc79c5be6fbbaab31c712e3f27 [file] [log] [blame]
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +05301/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 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 */
15
16#include <arch/acpigen.h>
17#include <device/i2c.h>
18#include <device/pci.h>
19#include <device/pci_def.h>
20#include <device/pci_ids.h>
21#include <intelblocks/lpss_i2c.h>
22#include "lpss_i2c.h"
23
24static int lpss_i2c_dev_to_bus(struct device *dev)
25{
26 pci_devfn_t devfn = dev->path.pci.devfn;
27 return i2c_soc_devfn_to_bus(devfn);
28}
29
30uintptr_t lpss_i2c_base_address(unsigned int bus)
31{
32 int devfn;
33 struct device *dev;
34 struct resource *res;
35
36 /* bus -> devfn */
37 devfn = i2c_soc_bus_to_devfn(bus);
38
39 if (devfn < 0)
40 return (uintptr_t)NULL;
41
42 /* devfn -> dev */
43 dev = dev_find_slot(0, devfn);
Furquan Shaikhd629e4332017-06-09 17:54:00 -070044 if (!dev || !dev->enabled)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053045 return (uintptr_t)NULL;
46
47 /* dev -> bar0 */
48 res = find_resource(dev, PCI_BASE_ADDRESS_0);
49 if (res)
50 return res->base;
51
52 return (uintptr_t)NULL;
53}
54
55/*
56 * Write ACPI object to describe speed configuration.
57 *
58 * ACPI Object: Name ("xxxx", Package () { scl_lcnt, scl_hcnt, sda_hold }
59 *
60 * SSCN: I2C_SPEED_STANDARD
61 * FMCN: I2C_SPEED_FAST
62 * FPCN: I2C_SPEED_FAST_PLUS
63 * HSCN: I2C_SPEED_HIGH
64 */
65static void lpss_i2c_acpi_write_speed_config(
66 const struct lpss_i2c_speed_config *config)
67{
68 if (!config)
69 return;
70 if (!config->scl_lcnt && !config->scl_hcnt && !config->sda_hold)
71 return;
72
73 if (config->speed >= I2C_SPEED_HIGH)
74 acpigen_write_name("HSCN");
75 else if (config->speed >= I2C_SPEED_FAST_PLUS)
76 acpigen_write_name("FPCN");
77 else if (config->speed >= I2C_SPEED_FAST)
78 acpigen_write_name("FMCN");
79 else
80 acpigen_write_name("SSCN");
81
82 /* Package () { scl_lcnt, scl_hcnt, sda_hold } */
83 acpigen_write_package(3);
84 acpigen_write_word(config->scl_hcnt);
85 acpigen_write_word(config->scl_lcnt);
86 acpigen_write_dword(config->sda_hold);
87 acpigen_pop_len();
88}
89
90/*
91 * The device should already be enabled and out of reset,
92 * either from early init in coreboot or SiliconInit in FSP.
93 */
94static void lpss_i2c_dev_init(struct device *dev)
95{
96 const struct lpss_i2c_bus_config *config;
97 int bus = lpss_i2c_dev_to_bus(dev);
98
99 config = i2c_get_soc_cfg(bus, dev);
100
101 if (!config || bus < 0)
102 return;
103
104 lpss_i2c_init(bus, config);
105}
106
107/*
108 * Generate I2C timing information into the SSDT for the OS driver to consume,
109 * optionally applying override values provided by the caller.
110 */
111static void lpss_i2c_acpi_fill_ssdt(struct device *dev)
112{
113 const struct lpss_i2c_bus_config *bcfg;
114 struct lpss_i2c_regs *regs;
115 struct lpss_i2c_speed_config sgen;
116 enum i2c_speed speeds[LPSS_I2C_SPEED_CONFIG_COUNT] = {
117 I2C_SPEED_STANDARD,
118 I2C_SPEED_FAST,
119 I2C_SPEED_FAST_PLUS,
120 I2C_SPEED_HIGH,
121 };
Furquan Shaikhd629e4332017-06-09 17:54:00 -0700122 int i, bus;
123
124 if (!dev->enabled)
125 return;
126
127 bus = lpss_i2c_dev_to_bus(dev);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530128
129 bcfg = i2c_get_soc_cfg(bus, dev);
130
131 if (!bcfg)
132 return;
133
134 regs = (struct lpss_i2c_regs *)lpss_i2c_base_address(bus);
135 if (!regs)
136 return;
137
138 acpigen_write_scope(acpi_device_path(dev));
139
140 /* Report timing values for the OS driver */
141 for (i = 0; i < LPSS_I2C_SPEED_CONFIG_COUNT; i++) {
142 /* Generate speed config. */
143 if (lpss_i2c_gen_speed_config(regs, speeds[i], bcfg, &sgen) < 0)
144 continue;
145
146 /* Generate ACPI based on selected speed config */
147 lpss_i2c_acpi_write_speed_config(&sgen);
148 }
149
150 acpigen_pop_len();
151}
152
153static struct i2c_bus_operations i2c_bus_ops = {
154 .dev_to_bus = &lpss_i2c_dev_to_bus,
155};
156
157static struct device_operations i2c_dev_ops = {
158 .read_resources = &pci_dev_read_resources,
159 .set_resources = &pci_dev_set_resources,
160 .enable_resources = &pci_dev_enable_resources,
161 .scan_bus = &scan_smbus,
162 .ops_i2c_bus = &i2c_bus_ops,
163 .init = &lpss_i2c_dev_init,
164 .acpi_fill_ssdt_generator = &lpss_i2c_acpi_fill_ssdt,
165};
166
167static const unsigned short pci_device_ids[] = {
168 PCI_DEVICE_ID_INTEL_SPT_I2C0,
169 PCI_DEVICE_ID_INTEL_SPT_I2C1,
170 PCI_DEVICE_ID_INTEL_SPT_I2C2,
171 PCI_DEVICE_ID_INTEL_SPT_I2C3,
172 PCI_DEVICE_ID_INTEL_SPT_I2C4,
173 PCI_DEVICE_ID_INTEL_SPT_I2C5,
174 PCI_DEVICE_ID_INTEL_APL_I2C0,
175 PCI_DEVICE_ID_INTEL_APL_I2C1,
176 PCI_DEVICE_ID_INTEL_APL_I2C2,
177 PCI_DEVICE_ID_INTEL_APL_I2C3,
178 PCI_DEVICE_ID_INTEL_APL_I2C4,
179 PCI_DEVICE_ID_INTEL_APL_I2C5,
180 PCI_DEVICE_ID_INTEL_APL_I2C6,
181 PCI_DEVICE_ID_INTEL_APL_I2C7,
Ravi Sarawadi3038e9b2017-05-18 16:00:35 -0700182 PCI_DEVICE_ID_INTEL_GLK_I2C0,
183 PCI_DEVICE_ID_INTEL_GLK_I2C1,
184 PCI_DEVICE_ID_INTEL_GLK_I2C2,
185 PCI_DEVICE_ID_INTEL_GLK_I2C3,
186 PCI_DEVICE_ID_INTEL_GLK_I2C4,
187 PCI_DEVICE_ID_INTEL_GLK_I2C5,
188 PCI_DEVICE_ID_INTEL_GLK_I2C6,
189 PCI_DEVICE_ID_INTEL_GLK_I2C7,
190 0,
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530191};
192
193static const struct pci_driver pch_i2c __pci_driver = {
194 .ops = &i2c_dev_ops,
195 .vendor = PCI_VENDOR_ID_INTEL,
196 .devices = pci_device_ids,
197};