blob: d9cf1b94340f8998f202eea99393dec73d24d138 [file] [log] [blame]
Angel Pons54c54722020-04-05 13:20:54 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -08002
3#include <types.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05004#include <cbfs.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -08005#include <device/device.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -08006#include <device/pci_ops.h>
7#include <console/console.h>
Vladimir Serbinenkoa2a906e2014-09-01 01:41:37 +02008#include <drivers/intel/gma/int15.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05009#include <fmap.h>
Furquan Shaikh76cedd22020-05-02 10:24:23 -070010#include <acpi/acpi.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -080011#include <arch/io.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -080012#include "onboard.h"
13#include "ec.h"
14#include <southbridge/intel/bd82x6x/pch.h>
15#include <smbios.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -080016#include <ec/quanta/ene_kb3940q/ec.h>
Aaron Durbinb0f81512016-07-25 21:31:41 -050017#include <vendorcode/google/chromeos/chromeos.h>
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -080018
19static unsigned int search(char *p, char *a, unsigned int lengthp,
20 unsigned int lengtha)
21{
22 int i, j;
23
24 /* Searching */
25 for (j = 0; j <= lengtha - lengthp; j++) {
Elyes HAOUASf2fcf222016-10-02 10:09:11 +020026 for (i = 0; i < lengthp && p[i] == a[i + j]; i++)
27 ;
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -080028 if (i >= lengthp)
29 return j;
30 }
31 return lengtha;
32}
33
34static unsigned char get_hex_digit(char *offset)
35{
36 unsigned char retval = 0;
37
38 retval = *offset - '0';
39 if (retval > 0x09) {
40 retval = *offset - 'A' + 0x0A;
41 if (retval > 0x0F)
42 retval = *offset - 'a' + 0x0a;
43 }
44 if (retval > 0x0F) {
45 printk(BIOS_DEBUG, "Error: Invalid Hex digit found: %c - 0x%02x\n",
46 *offset, (unsigned char)*offset);
47 retval = 0;
48 }
49
50 return retval;
51}
52
53static int get_mac_address(u32 *high_dword, u32 *low_dword,
54 u32 search_address, u32 search_length)
55{
56 char key[] = "ethernet_mac";
57 unsigned int offset;
58 int i;
59
60 offset = search(key, (char *)search_address,
61 sizeof(key) - 1, search_length);
62 if (offset == search_length) {
63 printk(BIOS_DEBUG,
64 "Error: Could not locate '%s' in VPD\n", key);
65 return 0;
66 }
67 printk(BIOS_DEBUG, "Located '%s' in VPD\n", key);
68
69 offset += sizeof(key); /* move to next character */
70 *high_dword = 0;
71
72 /* Fetch the MAC address and put the octets in the correct order to
73 * be programmed.
74 *
75 * From RTL8105E_Series_EEPROM-Less_App_Note_1.1
76 * If the MAC address is 001122334455h:
77 * Write 33221100h to I/O register offset 0x00 via double word access
78 * Write 00005544h to I/O register offset 0x04 via double word access
79 */
80
81 for (i = 0; i < 4; i++) {
82 *high_dword |= (get_hex_digit((char *)(search_address + offset))
83 << (4 + (i * 8)));
84 *high_dword |= (get_hex_digit((char *)(search_address + offset + 1))
85 << (i * 8));
86 offset += 3;
87 }
88
89 *low_dword = 0;
90 for (i = 0; i < 2; i++) {
91 *low_dword |= (get_hex_digit((char *)(search_address + offset))
92 << (4 + (i * 8)));
93 *low_dword |= (get_hex_digit((char *)(search_address + offset + 1))
94 << (i * 8));
95 offset += 3;
96 }
97
98 return *high_dword | *low_dword;
99}
100
101static void program_mac_address(u16 io_base, u32 search_address,
102 u32 search_length)
103{
104 /* Default MAC Address of A0:00:BA:D0:0B:AD */
105 u32 high_dword = 0xD0BA00A0; /* high dword of mac address */
106 u32 low_dword = 0x0000AD0B; /* low word of mac address as a dword */
107
108 if (search_length != -1)
109 get_mac_address(&high_dword, &low_dword, search_address,
110 search_length);
111
112 if (io_base) {
113 printk(BIOS_DEBUG, "Realtek NIC io_base = 0x%04x\n", io_base);
114 printk(BIOS_DEBUG, "Programming MAC Address\n");
115
116 outb(0xc0, io_base + 0x50); /* Disable register protection */
117 outl(high_dword, io_base);
118 outl(low_dword, io_base + 0x04);
119 outb(0x60, io_base + 54);
120 outb(0x00, io_base + 0x50); /* Enable register protection again */
121 }
122}
123
124static void program_keyboard_type(u32 search_address, u32 search_length)
125{
126 char key[] = "keyboard_layout";
127 char kbd_jpn[] = "xkb:jp::jpn";
128 unsigned int offset;
129 char kbd_type = EC_KBD_EN; /* Default keyboard type is English */
130
131 if (search_length != -1) {
132
133 /*
134 * Search for keyboard_layout identifier
135 * The only options in the EC are Japanese or English.
136 * The English keyboard layout is actually used for multiple
137 * different languages - English, Spanish, French... Because
138 * of this the code only searches for Japanese, and sets the
139 * keyboard type to English if Japanese is not found.
140 */
141 offset = search(key, (char *)search_address, sizeof(key) - 1,
142 search_length);
143 if (offset != search_length) {
144 printk(BIOS_DEBUG, "Located '%s' in VPD\n", key);
145
146 offset += sizeof(key); /* move to next character */
147 search_length = sizeof(kbd_jpn);
148 offset = search(kbd_jpn, (char *)(search_address + offset),
149 sizeof(kbd_jpn) - 1, search_length);
150 if (offset != search_length)
151 kbd_type = EC_KBD_JP;
152 }
153 } else
154 printk(BIOS_DEBUG, "Error: Could not locate VPD area\n");
155
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800156 printk(BIOS_DEBUG, "Setting Keyboard type in EC to ");
157 printk(BIOS_DEBUG, (kbd_type == EC_KBD_JP) ? "Japanese" : "English");
158 printk(BIOS_DEBUG, ".\n");
159
160 ec_mem_write(EC_KBID_REG, kbd_type);
161}
162
Elyes HAOUASd129d432018-05-04 20:23:33 +0200163static void mainboard_init(struct device *dev)
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800164{
Kyösti Mälkkifab0c9f2013-11-28 18:10:03 +0200165 u32 search_address = 0x0;
Vladimir Serbinenko12874162014-01-12 14:12:15 +0100166 size_t search_length = -1;
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800167 u16 io_base = 0;
168 struct device *ethernet_dev = NULL;
Aaron Durbin0424c952015-03-28 23:56:22 -0500169 void *vpd_file;
170
Julius Wernercd49cce2019-03-05 16:53:33 -0800171 if (CONFIG(CHROMEOS)) {
Aaron Durbin0424c952015-03-28 23:56:22 -0500172 struct region_device rdev;
173
174 if (fmap_locate_area_as_rdev("RO_VPD", &rdev) == 0) {
175 vpd_file = rdev_mmap_full(&rdev);
176
177 if (vpd_file != NULL) {
178 search_length = region_device_sz(&rdev);
179 search_address = (uintptr_t)vpd_file;
180 }
181 }
Vladimir Serbinenko12874162014-01-12 14:12:15 +0100182 } else {
Julius Werner834b3ec2020-03-04 16:52:08 -0800183 vpd_file = cbfs_map("vpd.bin", &search_length);
Aaron Durbin0424c952015-03-28 23:56:22 -0500184 if (vpd_file) {
185 search_address = (unsigned long)vpd_file;
186 } else {
187 search_length = -1;
188 search_address = 0;
189 }
Kyösti Mälkkifab0c9f2013-11-28 18:10:03 +0200190 }
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800191
192 /* Initialize the Embedded Controller */
193 butterfly_ec_init();
194
195 /* Program EC Keyboard locale based on VPD data */
196 program_keyboard_type(search_address, search_length);
197
198 /* Get NIC's IO base address */
199 ethernet_dev = dev_find_device(BUTTERFLY_NIC_VENDOR_ID,
200 BUTTERFLY_NIC_DEVICE_ID, dev);
201 if (ethernet_dev != NULL) {
202 io_base = pci_read_config16(ethernet_dev, 0x10) & 0xfffe;
203
204 /*
205 * Battery life time - LAN PCIe should enter ASPM L1 to save
206 * power when LAN connection is idle.
207 * enable CLKREQ: LAN pci config space 0x81h=01
208 */
209 pci_write_config8(ethernet_dev, 0x81, 0x01);
210 }
211
212 if (io_base) {
213 /* Program MAC address based on VPD data */
214 program_mac_address(io_base, search_address, search_length);
215
216 /*
217 * Program NIC LEDS
218 *
219 * RTL8105E Series EEPROM-Less Application Note,
220 * Section 5.6 LED Mode Configuration
221 *
222 * Step1: Write C0h to I/O register 0x50 via byte access to
223 * disable 'register protection'
224 * Step2: Write xx001111b to I/O register 0x52 via byte access
225 * (bit7 is LEDS1 and bit6 is LEDS0)
226 * Step3: Write 0x00 to I/O register 0x50 via byte access to
227 * enable 'register protection'
228 */
229 outb(0xc0, io_base + 0x50); /* Disable protection */
230 outb((BUTTERFLY_NIC_LED_MODE << 6) | 0x0f, io_base + 0x52);
231 outb(0x00, io_base + 0x50); /* Enable register protection */
232 }
233}
234
Elyes HAOUASd129d432018-05-04 20:23:33 +0200235static int butterfly_onboard_smbios_data(struct device *dev, int *handle,
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800236 unsigned long *current)
237{
238 int len = 0;
239
Duncan Laurie21a78702013-05-23 14:17:05 -0700240 len += smbios_write_type41(
241 current, handle,
Kyösti Mälkkib9cd5ec2015-04-24 16:05:58 +0300242 BOARD_TRACKPAD_NAME, /* name */
243 BOARD_TRACKPAD_IRQ, /* instance */
Duncan Laurie21a78702013-05-23 14:17:05 -0700244 0, /* segment */
Kyösti Mälkkib9cd5ec2015-04-24 16:05:58 +0300245 BOARD_TRACKPAD_I2C_ADDR, /* bus */
Duncan Laurie21a78702013-05-23 14:17:05 -0700246 0, /* device */
Christian Waltere6afab12019-05-21 17:22:49 +0200247 0, /* function */
248 SMBIOS_DEVICE_TYPE_OTHER); /* device type */
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800249
250 return len;
251}
252
253// mainboard_enable is executed as first thing after
254// enumerate_buses().
255
Elyes HAOUASd129d432018-05-04 20:23:33 +0200256static void mainboard_enable(struct device *dev)
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800257{
258 dev->ops->init = mainboard_init;
259 dev->ops->get_smbios_data = butterfly_onboard_smbios_data;
Nico Huber68680dd2020-03-31 17:34:52 +0200260 dev->ops->acpi_inject_dsdt = chromeos_dsdt_generator;
Vladimir Serbinenkoa2a906e2014-09-01 01:41:37 +0200261 install_intel_vga_int15_handler(GMA_INT15_ACTIVE_LFP_INT_LVDS, GMA_INT15_PANEL_FIT_DEFAULT, GMA_INT15_BOOT_DISPLAY_DEFAULT, 0);
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800262}
263
264struct chip_operations mainboard_ops = {
Stefan Reinauerd7bd4eb2013-02-11 11:11:36 -0800265 .enable_dev = mainboard_enable,
266};