| /* |
| * Copyright 2013, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <limits.h> /* for INT_MAX */ |
| |
| #include <arpa/inet.h> /* ntohl() */ |
| |
| #include "mosys/alloc.h" |
| #include "mosys/callbacks.h" |
| #include "mosys/globals.h" |
| #include "mosys/globals.h" |
| #include "mosys/log.h" |
| #include "mosys/platform.h" |
| |
| #include "drivers/gpio.h" |
| #include "drivers/intel/baytrail.h" |
| |
| #include "lib/cbfs_core.h" |
| #include "lib/file.h" |
| #include "lib/flashrom.h" |
| #include "lib/math.h" |
| #include "lib/spd.h" |
| |
| #include "rambi.h" |
| |
| #define RAMBI_DIMM_COUNT 2 |
| |
| /* |
| * Return the number of RAM_ID strap GPIOs. These GPIOs are standardized |
| * across all Rambi-class platforms, but some platforms have four GPIOs |
| * instead of three because they must support > 8 DRAM SKUs. |
| */ |
| static int rambi_ramid_gpio_count(struct platform_intf *intf) |
| { |
| int gpio_count; |
| |
| if (!strncmp(intf->name, "Glimmer", 7)) |
| gpio_count = 4; |
| else |
| gpio_count = 3; |
| |
| return gpio_count; |
| } |
| |
| /* |
| * SPD blob contains up to eight entries which are selected by |
| * board strappings. |
| */ |
| static int rambi_get_spd_index(struct platform_intf *intf) |
| { |
| int spd_index = 0; |
| int gpio_count = rambi_ramid_gpio_count(intf); |
| int val; |
| int i; |
| struct gpio_map ram_id[] = { { 37, GPIO_IN, 0, BAYTRAIL_GPSSUS_PORT }, |
| { 38, GPIO_IN, 0, BAYTRAIL_GPSSUS_PORT }, |
| { 39, GPIO_IN, 0, BAYTRAIL_GPSSUS_PORT }, |
| { 40, GPIO_IN, 0, BAYTRAIL_GPSSUS_PORT } }; |
| |
| for (i = 0; i < gpio_count; i++) { |
| if ((val = intf->cb->gpio->read(intf, &ram_id[i])) < 0) |
| return -1; |
| spd_index |= val << i; |
| } |
| |
| return spd_index; |
| } |
| |
| /* |
| * rambi_dimm_count - return total number of dimm slots |
| * |
| * @intf: platform interface |
| * |
| * returns dimm slot count |
| */ |
| static int rambi_dimm_count(struct platform_intf *intf) |
| { |
| /* TODO(kevin.cheng): Find a programatic way to do this detection */ |
| if (!strncmp(intf->name, "Glimmer", 7)) { |
| /* Glimmer has 1 or 2 DIMM config base on RAM_ID. |
| * {0,0,0,0} = 2 x 2GiB Micron |
| * {0,0,0,1} = 2 x 2GiB Hynix |
| * {0,0,1,0} = 2 x 1GiB Micron |
| * {0,0,1,1} = 2 x 1GiB Hynix |
| * {0,1,0,0} = 2 x 1GiB Samsung |
| * {0,1,0,1} = 1 x 2GiB Hynix |
| * {0,1,1,0} = 2 x 2GiB Samsung |
| * {0,1,1,1} = 2 x 2GiB Elpida |
| * {1,0,0,0} = 1 x 2GiB Micron |
| * {1,0,0,1} = 1 x 2GiB Elpida |
| * {1,0,1,0} = 1 x 2GiB Samsung |
| */ |
| int index = rambi_get_spd_index(intf); |
| switch (index) { |
| case 5: case 8: case 9: case 10: |
| return 1; |
| default: |
| return 2; |
| } |
| } else if (!strncmp(intf->name, "Clapper", 7)) { |
| /* Clapper has 1 or 2 DIMM config base on RAM_ID. |
| * {0,0,0} = 2 x 2GiB Micron |
| * {0,0,1} = 2 x 2GiB Hynix |
| * {0,1,0} = 2 x 1GiB Micron |
| * {0,1,1} = 2 x 1GiB Hynix |
| * {1,0,0} = 1 x 2GiB Micron |
| * {1,0,1} = 1 x 2GiB Hynix |
| * {1,1,0} = 2 x 2GiB Samsung |
| * {1,1,1} = 1 x 2GiB Samsung |
| */ |
| int index = rambi_get_spd_index(intf); |
| switch (index) { |
| case 4: case 5: case 7: |
| return 1; |
| default: |
| return 2; |
| } |
| } |
| return RAMBI_DIMM_COUNT; |
| } |
| |
| static int rambi_spd_read_cbfs(struct platform_intf *intf, |
| int dimm, int reg, int len, uint8_t *buf) |
| { |
| static int first_run = 1; |
| static uint8_t *bootblock = NULL; |
| size_t size = RAMBI_HOST_FIRMWARE_ROM_SIZE; |
| struct cbfs_file *file; |
| int spd_index = 0; |
| uint32_t spd_offset; |
| |
| if (dimm > rambi_dimm_count(intf)) { |
| lprintf(LOG_DEBUG, "%s: Invalid DIMM specified\n", __func__); |
| return -1; |
| } |
| |
| if (first_run) { |
| bootblock = mosys_malloc(size); /* FIXME: overkill */ |
| add_destroy_callback(free, bootblock); |
| first_run = 0; |
| |
| /* read SPD from CBFS entry located within bootblock region */ |
| if (flashrom_read(bootblock, size, |
| INTERNAL_BUS_SPI, "BOOT_STUB") < 0) |
| return -1; |
| } |
| |
| if ((file = cbfs_find("spd.bin", bootblock, size)) == NULL) |
| return -1; |
| |
| spd_index = rambi_get_spd_index(intf); |
| if (spd_index < 0) |
| return -1; |
| |
| spd_offset = ntohl(file->offset) + (spd_index * 256); |
| lprintf(LOG_DEBUG, "Using memory config %u\n", spd_index); |
| memcpy(buf, (void *)file + spd_offset + reg, len); |
| |
| return len; |
| } |
| |
| static int rambi_spd_read(struct platform_intf *intf, |
| int dimm, int reg, int len, uint8_t *buf) |
| { |
| return rambi_spd_read_cbfs(intf, dimm, reg, len, buf); |
| } |
| |
| static struct memory_spd_cb rambi_spd_cb = { |
| .read = rambi_spd_read, |
| }; |
| |
| struct memory_cb rambi_memory_cb = { |
| .dimm_count = rambi_dimm_count, |
| .spd = &rambi_spd_cb, |
| }; |