blob: 4b2b4eb5a88c73b946fc196d746905351dad33a8 [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>
Nico Huber0f2dd1e2017-08-01 14:02:40 +020017#include <device/i2c_bus.h>
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053018#include <device/pci.h>
19#include <device/pci_def.h>
20#include <device/pci_ids.h>
Chris Chingb8dc63b2017-12-06 14:26:15 -070021#include <drivers/i2c/designware/dw_i2c.h>
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053022
Chris Chingb8dc63b2017-12-06 14:26:15 -070023uintptr_t dw_i2c_base_address(unsigned int bus)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053024{
25 int devfn;
26 struct device *dev;
27 struct resource *res;
28
29 /* bus -> devfn */
Aaron Durbin9aee8192018-01-22 20:29:25 -070030 devfn = dw_i2c_soc_bus_to_devfn(bus);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053031
32 if (devfn < 0)
33 return (uintptr_t)NULL;
34
35 /* devfn -> dev */
36 dev = dev_find_slot(0, devfn);
Furquan Shaikhd629e4332017-06-09 17:54:00 -070037 if (!dev || !dev->enabled)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053038 return (uintptr_t)NULL;
39
40 /* dev -> bar0 */
41 res = find_resource(dev, PCI_BASE_ADDRESS_0);
42 if (res)
43 return res->base;
44
45 return (uintptr_t)NULL;
46}
47
Chris Chingb8dc63b2017-12-06 14:26:15 -070048static int lpss_i2c_dev_to_bus(struct device *dev)
49{
50 pci_devfn_t devfn = dev->path.pci.devfn;
Aaron Durbin9aee8192018-01-22 20:29:25 -070051 return dw_i2c_soc_devfn_to_bus(devfn);
Chris Chingb8dc63b2017-12-06 14:26:15 -070052}
53
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053054/*
55 * Write ACPI object to describe speed configuration.
56 *
57 * ACPI Object: Name ("xxxx", Package () { scl_lcnt, scl_hcnt, sda_hold }
58 *
59 * SSCN: I2C_SPEED_STANDARD
60 * FMCN: I2C_SPEED_FAST
61 * FPCN: I2C_SPEED_FAST_PLUS
62 * HSCN: I2C_SPEED_HIGH
63 */
64static void lpss_i2c_acpi_write_speed_config(
Chris Chingb8dc63b2017-12-06 14:26:15 -070065 const struct dw_i2c_speed_config *config)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053066{
67 if (!config)
68 return;
69 if (!config->scl_lcnt && !config->scl_hcnt && !config->sda_hold)
70 return;
71
72 if (config->speed >= I2C_SPEED_HIGH)
73 acpigen_write_name("HSCN");
74 else if (config->speed >= I2C_SPEED_FAST_PLUS)
75 acpigen_write_name("FPCN");
76 else if (config->speed >= I2C_SPEED_FAST)
77 acpigen_write_name("FMCN");
78 else
79 acpigen_write_name("SSCN");
80
81 /* Package () { scl_lcnt, scl_hcnt, sda_hold } */
82 acpigen_write_package(3);
83 acpigen_write_word(config->scl_hcnt);
84 acpigen_write_word(config->scl_lcnt);
85 acpigen_write_dword(config->sda_hold);
86 acpigen_pop_len();
87}
88
89/*
90 * The device should already be enabled and out of reset,
91 * either from early init in coreboot or SiliconInit in FSP.
92 */
93static void lpss_i2c_dev_init(struct device *dev)
94{
Chris Chingb8dc63b2017-12-06 14:26:15 -070095 const struct dw_i2c_bus_config *config;
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +053096 int bus = lpss_i2c_dev_to_bus(dev);
97
Rizwan Qureshi7f72c642017-10-04 18:53:19 +053098 if (bus < 0)
99 return;
100
Aaron Durbin9aee8192018-01-22 20:29:25 -0700101 config = dw_i2c_get_soc_cfg(bus, dev);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530102
Rizwan Qureshi7f72c642017-10-04 18:53:19 +0530103 if (!config)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530104 return;
105
Chris Chingb8dc63b2017-12-06 14:26:15 -0700106 dw_i2c_init(bus, config);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530107}
108
109/*
110 * Generate I2C timing information into the SSDT for the OS driver to consume,
111 * optionally applying override values provided by the caller.
112 */
113static void lpss_i2c_acpi_fill_ssdt(struct device *dev)
114{
Chris Chingb8dc63b2017-12-06 14:26:15 -0700115 const struct dw_i2c_bus_config *bcfg;
116 uintptr_t dw_i2c_addr;
117 struct dw_i2c_speed_config sgen;
118 enum i2c_speed speeds[DW_I2C_SPEED_CONFIG_COUNT] = {
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530119 I2C_SPEED_STANDARD,
120 I2C_SPEED_FAST,
121 I2C_SPEED_FAST_PLUS,
122 I2C_SPEED_HIGH,
123 };
Furquan Shaikhd629e4332017-06-09 17:54:00 -0700124 int i, bus;
Shaunak Saha5a441762017-12-12 10:21:50 -0800125 const char *path;
Furquan Shaikhd629e4332017-06-09 17:54:00 -0700126
127 if (!dev->enabled)
128 return;
129
130 bus = lpss_i2c_dev_to_bus(dev);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530131
Rizwan Qureshi7f72c642017-10-04 18:53:19 +0530132 if (bus < 0)
133 return;
134
Aaron Durbin9aee8192018-01-22 20:29:25 -0700135 bcfg = dw_i2c_get_soc_cfg(bus, dev);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530136
137 if (!bcfg)
138 return;
139
Chris Chingb8dc63b2017-12-06 14:26:15 -0700140 dw_i2c_addr = dw_i2c_base_address(bus);
141 if (!dw_i2c_addr)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530142 return;
143
Shaunak Saha5a441762017-12-12 10:21:50 -0800144 path = acpi_device_path(dev);
145 if (!path)
146 return;
147
148 acpigen_write_scope(path);
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530149
150 /* Report timing values for the OS driver */
Chris Chingb8dc63b2017-12-06 14:26:15 -0700151 for (i = 0; i < DW_I2C_SPEED_CONFIG_COUNT; i++) {
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530152 /* Generate speed config. */
Chris Chingb8dc63b2017-12-06 14:26:15 -0700153 if (dw_i2c_gen_speed_config(dw_i2c_addr, speeds[i], bcfg,
154 &sgen) < 0)
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530155 continue;
156
157 /* Generate ACPI based on selected speed config */
158 lpss_i2c_acpi_write_speed_config(&sgen);
159 }
160
161 acpigen_pop_len();
162}
163
Nico Huber58173862017-08-01 17:09:35 +0200164static int lpss_i2c_dev_transfer(struct device *dev,
165 const struct i2c_msg *msg, size_t count)
166{
Chris Chingb8dc63b2017-12-06 14:26:15 -0700167 return dw_i2c_transfer(lpss_i2c_dev_to_bus(dev), msg, count);
Nico Huber58173862017-08-01 17:09:35 +0200168}
169
170static const struct i2c_bus_operations i2c_bus_ops = {
171 .transfer = lpss_i2c_dev_transfer,
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530172};
173
174static struct device_operations i2c_dev_ops = {
175 .read_resources = &pci_dev_read_resources,
176 .set_resources = &pci_dev_set_resources,
177 .enable_resources = &pci_dev_enable_resources,
178 .scan_bus = &scan_smbus,
179 .ops_i2c_bus = &i2c_bus_ops,
Subrata Banik6bbc91a2017-12-07 14:55:51 +0530180 .ops_pci = &pci_dev_ops_pci,
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530181 .init = &lpss_i2c_dev_init,
182 .acpi_fill_ssdt_generator = &lpss_i2c_acpi_fill_ssdt,
183};
184
185static const unsigned short pci_device_ids[] = {
186 PCI_DEVICE_ID_INTEL_SPT_I2C0,
187 PCI_DEVICE_ID_INTEL_SPT_I2C1,
188 PCI_DEVICE_ID_INTEL_SPT_I2C2,
189 PCI_DEVICE_ID_INTEL_SPT_I2C3,
190 PCI_DEVICE_ID_INTEL_SPT_I2C4,
191 PCI_DEVICE_ID_INTEL_SPT_I2C5,
192 PCI_DEVICE_ID_INTEL_APL_I2C0,
193 PCI_DEVICE_ID_INTEL_APL_I2C1,
194 PCI_DEVICE_ID_INTEL_APL_I2C2,
195 PCI_DEVICE_ID_INTEL_APL_I2C3,
196 PCI_DEVICE_ID_INTEL_APL_I2C4,
197 PCI_DEVICE_ID_INTEL_APL_I2C5,
198 PCI_DEVICE_ID_INTEL_APL_I2C6,
199 PCI_DEVICE_ID_INTEL_APL_I2C7,
Lijian Zhaobbedef92017-07-29 16:38:38 -0700200 PCI_DEVICE_ID_INTEL_CNL_I2C0,
201 PCI_DEVICE_ID_INTEL_CNL_I2C1,
202 PCI_DEVICE_ID_INTEL_CNL_I2C2,
203 PCI_DEVICE_ID_INTEL_CNL_I2C3,
204 PCI_DEVICE_ID_INTEL_CNL_I2C4,
205 PCI_DEVICE_ID_INTEL_CNL_I2C5,
Ravi Sarawadi3038e9b2017-05-18 16:00:35 -0700206 PCI_DEVICE_ID_INTEL_GLK_I2C0,
207 PCI_DEVICE_ID_INTEL_GLK_I2C1,
208 PCI_DEVICE_ID_INTEL_GLK_I2C2,
209 PCI_DEVICE_ID_INTEL_GLK_I2C3,
210 PCI_DEVICE_ID_INTEL_GLK_I2C4,
211 PCI_DEVICE_ID_INTEL_GLK_I2C5,
212 PCI_DEVICE_ID_INTEL_GLK_I2C6,
213 PCI_DEVICE_ID_INTEL_GLK_I2C7,
214 0,
Rizwan Qureshiae6a4b62017-04-26 21:06:35 +0530215};
216
217static const struct pci_driver pch_i2c __pci_driver = {
218 .ops = &i2c_dev_ops,
219 .vendor = PCI_VENDOR_ID_INTEL,
220 .devices = pci_device_ids,
221};