| /* |
| * NDA AND NEED-TO-KNOW REQUIRED |
| * |
| * Copyright © 2013-2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This file contains information that is proprietary to Synaptics |
| * Incorporated ("Synaptics"). The holder of this file shall treat all |
| * information contained herein as confidential, shall use the |
| * information only for its intended purpose, and shall not duplicate, |
| * disclose, or disseminate any of this information in any manner |
| * unless Synaptics has otherwise provided express, written |
| * permission. |
| * |
| * Use of the materials may require a license of intellectual property |
| * from a third party or from Synaptics. This file conveys no express |
| * or implied licenses to any intellectual property rights belonging |
| * to Synaptics. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| |
| #include <stdint.h> |
| #include "libfdt.h" |
| #include "libfdt_mrvl.h" |
| #include "string.h" |
| #include "debug.h" |
| #include "lgpl_printf.h" |
| #include "pmic_select.h" |
| |
| /* |
| * Add or modify node into the provided FDT. |
| * add ramdisk addr/ramdisk size and modify cmdline to FDT. |
| * REturn values: |
| * = 0 -> pretend success |
| */ |
| int set_fdt(void *fdt, int total_space,unsigned int initrd_start,unsigned int initrd_size,char * cmdline) |
| { |
| int ret; |
| int offset = 0; |
| |
| /* let's give it all the room it could need */ |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if (ret < 0) |
| return ret; |
| |
| /* find the offset in FDT */ |
| offset = fdt_path_offset(fdt,"/chosen"); |
| if (offset == -FDT_ERR_NOTFOUND){ |
| offset = fdt_add_subnode(fdt, 0, "/chosen"); |
| if (offset < 0) |
| return offset; |
| } |
| |
| /* modify the bootargs in FDT */ |
| if(cmdline != NULL) |
| { |
| ret = fdt_setprop_string(fdt, offset, "bootargs", cmdline); |
| if(ret) |
| return ret; |
| } |
| |
| if(initrd_size != 0) { |
| /* add ramdisk addr/ramdisk size to FDT */ |
| ret = fdt_setprop_cell(fdt, offset,"linux,initrd-start",initrd_start); |
| if(ret) |
| return ret; |
| ret = fdt_setprop_cell(fdt, offset,"linux,initrd-end",initrd_start + initrd_size); |
| if(ret) |
| return ret; |
| } |
| |
| return fdt_pack(fdt); |
| } |
| |
| static unsigned int g_fdt = 0; |
| static int g_total_space = 0; |
| |
| static int check_fdt_validate(void) |
| { |
| if((g_fdt == 0) || (g_total_space <= 0)) |
| return FDT_ERR_NOTFOUND; |
| |
| return 0; |
| } |
| void fdt_set_reserved_mem(void *fdt, unsigned int address, unsigned int size) |
| { |
| struct fdt_header * header = (struct fdt_header *)fdt; |
| unsigned int offset = fdt32_to_cpu(header->off_mem_rsvmap); |
| struct fdt_reserve_entry * mem_reserve = (struct fdt_reserve_entry *)(((unsigned char *)fdt) + offset); |
| if(mem_reserve->size != 0) { |
| mem_reserve->address = cpu_to_fdt64(address); |
| mem_reserve->size = cpu_to_fdt64(size); |
| } |
| } |
| |
| /* set the address and size of dtb first */ |
| void set_fdt_addr(unsigned int fdt, int total_space) |
| { |
| g_fdt = fdt; |
| g_total_space = total_space; |
| } |
| |
| int get_fdt_addr(unsigned int *fdt, int * total_space) |
| { |
| if(check_fdt_validate() == 0) { |
| *fdt = g_fdt; |
| *total_space = g_total_space; |
| |
| return 0; |
| } |
| |
| return FDT_ERR_NOTFOUND; |
| } |
| |
| /* pass system info (rkek_id, chip_ver) to kernel.*/ |
| int fdt_add_system_info(void *fdt, |
| int total_space, |
| const char *pnode, |
| const system_info_t *system_info) |
| { |
| int ret; |
| int offset = 0; |
| char buf[128] = {0}; |
| |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if (ret < 0) |
| return ret; |
| |
| offset = fdt_path_offset(fdt, pnode); |
| if (offset == -FDT_ERR_NOTFOUND){ |
| offset = fdt_add_subnode(fdt, 0, pnode); |
| if (offset < 0) |
| return offset; |
| } |
| |
| sprintf(buf, "%08x\n", system_info->chip_ver); |
| ret = fdt_setprop(fdt, offset, "system_rev", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x%08x\n", system_info->system_serial_low, system_info->system_serial_high); |
| ret = fdt_setprop(fdt, offset, "system_serial", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "Revision\t: %08x\nSerial\t\t: %08x%08x\n", system_info->chip_ver, system_info->system_serial_low, system_info->system_serial_high); |
| ret = fdt_setprop(fdt, offset, "system_info", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->leakage_current); |
| ret = fdt_setprop(fdt, offset, "leakage_current", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->temp_id); |
| ret = fdt_setprop(fdt, offset, "temp_id", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->leakage_current_v1); |
| ret = fdt_setprop(fdt, offset, "leakage_current_v1", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->temp_id_v1); |
| ret = fdt_setprop(fdt, offset, "temp_id_v1", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->leakage_current_v2); |
| ret = fdt_setprop(fdt, offset, "leakage_current_v2", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->temp_id_v2); |
| ret = fdt_setprop(fdt, offset, "temp_id_v2", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%u\n", system_info->chip_timestamp); |
| ret = fdt_setprop(fdt, offset, "chip_timestamp", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| sprintf(buf, "%u\n", system_info->pcie_disable); |
| ret = fdt_setprop(fdt, offset, "pcie_disable", buf, strlen(buf) + 1); |
| if (ret) |
| return ret; |
| |
| sprintf(buf, "%08x\n", system_info->pvcomp_rev); |
| ret = fdt_setprop(fdt, offset, "pvcomp_rev", buf, strlen(buf) + 1); |
| if(ret) |
| return ret; |
| |
| return fdt_pack(fdt); |
| } |
| |
| /* update the i2c slave address of pmic */ |
| int fdt_update_i2c_slave_addr(void *fdt, int total_space, unsigned int masterid, unsigned int slave_addr) |
| { |
| int ret; |
| int offset = 0; |
| unsigned int reg = 0; |
| |
| /* let's give it all the room it could need */ |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if (ret < 0) |
| return ret; |
| |
| if(masterid == 0) |
| offset = fdt_path_offset(fdt, "/soc/apb@e80000/i2c@1400/pg867"); |
| else |
| offset = fdt_path_offset(fdt, "/soc/apb@e80000/i2c@3400/pg867"); |
| if (offset == -FDT_ERR_NOTFOUND) { |
| lgpl_printf("didn't find point of i2c\n"); |
| return -1; |
| } |
| |
| reg = cpu_to_fdt32(slave_addr); |
| |
| fdt_setprop(fdt, offset, "reg", ®, sizeof(unsigned int)); |
| |
| return fdt_pack(fdt); |
| } |
| |
| |
| /* pass opp table to kernel */ |
| int fdt_add_opp(void *fdt, int total_space, unsigned int * opp, int count) |
| { |
| int ret; |
| int offset = 0, i = 0; |
| |
| if(count == 0) { |
| lgpl_printf("opp table is NULL!!!!\n"); |
| return -1; |
| } |
| |
| for(i = 0; i < count; i++) { |
| opp[i] = cpu_to_fdt32(opp[i]); |
| } |
| |
| /* let's give it all the room it could need */ |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if (ret < 0) |
| return ret; |
| |
| offset = fdt_path_offset(fdt, "/cpus/cpu@0"); |
| if (offset == -FDT_ERR_NOTFOUND) { |
| lgpl_printf("didn't find point of cpus\n"); |
| return -1; |
| } |
| |
| fdt_setprop(fdt, offset, "operating-points", opp, sizeof(unsigned int) * count); |
| return fdt_pack(fdt); |
| } |
| |
| static void update_opp(void *buf, int offset, unsigned int vh, unsigned int vl) |
| { |
| int node; |
| const unsigned int *p; |
| unsigned int v1, v3, vol_h[3], vol_l[3]; |
| |
| /* opp-microvolt = <vh vh vh>; */ |
| vol_h[2] = vol_h[1] = vol_h[0] = cpu_to_fdt32(vh); |
| |
| /* opp-microvolt = <vl vl vh>; */ |
| vol_l[2] = cpu_to_fdt32(vh); |
| vol_l[1] = vol_l[0] = cpu_to_fdt32(vl); |
| |
| for(node = fdt_first_subnode(buf, offset); node >= 0; |
| node = fdt_next_subnode(buf, node)) { |
| p = fdt_getprop(buf, node, "opp-microvolt", NULL); |
| if (!p) |
| continue; |
| v1 = fdt32_to_cpu(*p); |
| p += 2; |
| v3 = fdt32_to_cpu(*p); |
| if (v1 < v3) |
| fdt_setprop(buf, node, "opp-microvolt", vol_l, sizeof(vol_l)); |
| else |
| fdt_setprop(buf, node, "opp-microvolt", vol_h, sizeof(vol_h)); |
| } |
| } |
| |
| void fdt_set_vcpu_opp(void * fdt, unsigned int vh, unsigned int vl) |
| { |
| int offset; |
| unsigned int vcpuh, vcpul, vcoreh, vcorel; |
| vcpuh = vcoreh = vh; |
| vcpul = vcorel = vl; |
| |
| offset = fdt_path_offset(fdt, "/cpus/opp_table0"); |
| if (offset < 0) { |
| lgpl_printf("didn't find point of /cpus/opp_table0\n"); |
| return; |
| } |
| |
| update_opp(fdt, offset, vcpuh, vcpul); |
| |
| offset = fdt_node_offset_by_compatible(fdt, offset, |
| "operating-points-v2"); |
| while (offset != -FDT_ERR_NOTFOUND) { |
| update_opp(fdt, offset, vcoreh, vcorel); |
| offset = fdt_node_offset_by_compatible(fdt, offset, |
| "operating-points-v2"); |
| } |
| } |
| |
| void fdt_set_vcore_opp(void * fdt, unsigned int vcoreh, unsigned int vcorel) |
| { |
| int offset; |
| |
| offset = fdt_path_offset(fdt, "/soc/vcore_opp_table"); |
| if (offset < 0) { |
| lgpl_printf("didn't find point of /soc/vcore_opp_table\n"); |
| return; |
| } |
| |
| update_opp(fdt, offset, vcoreh, vcorel); |
| } |
| |
| int fdt_set_leakage(void *fdt, int total_space, unsigned int leakage_num) |
| { |
| int ret; |
| int offset = 0; |
| unsigned int leakage = 0x0; |
| |
| /* let's give it all the room it could need */ |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if(ret < 0) |
| return ret; |
| |
| /* find the offset in FDT*/ |
| offset = fdt_path_offset(fdt, "/soc/cpm"); |
| if(offset == -FDT_ERR_NOTFOUND) { |
| lgpl_printf("didn't find point of /soc/cpm\n"); |
| return offset; |
| } |
| |
| leakage = cpu_to_fdt32(leakage_num); |
| fdt_setprop(fdt, offset, "leakage", (const void *)(uintptr_t)(&leakage), sizeof(leakage)); |
| |
| return fdt_pack(fdt); |
| } |
| |
| int fdt_set_chip_rev(void *fdt, int total_space, unsigned int chip_rev) |
| { |
| int ret; |
| int offset = 0; |
| unsigned int chip_revision = 0x0; |
| |
| /* let's give it all the room it could need */ |
| ret = fdt_open_into(fdt, fdt, total_space); |
| if(ret < 0) |
| return ret; |
| |
| /* find the offset in FDT*/ |
| offset = fdt_path_offset(fdt, "/soc/chipid@ea0000"); |
| if(offset == -FDT_ERR_NOTFOUND) { |
| lgpl_printf("didn't find point of /soc/chipid@ea0000\n"); |
| return offset; |
| } |
| |
| chip_revision = cpu_to_fdt32(chip_rev); |
| fdt_setprop(fdt, offset, "chip-revision", (const void *)(uintptr_t)(&chip_revision), sizeof(chip_revision)); |
| |
| return fdt_pack(fdt); |
| } |
| |
| void fdt_select_pmic(void *fdt) |
| { |
| int offset = 0; |
| unsigned int pmic_id = get_pmic_id(); |
| |
| if (SY8824B == pmic_id) { |
| lgpl_printf("Select PMIC chip sy8824b for Kernel !\n"); |
| |
| /* enable SY8824 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy8824"); |
| while(offset > 0) { |
| lgpl_printf("Update silergy,sy8824!\n"); |
| uint32_t phandle = fdt_get_phandle(fdt, offset); |
| fdt_setprop_string(fdt, offset, "status", "okay"); |
| if((phandle != (uint32_t)(-1)) && phandle) { |
| int cpu0_offset = fdt_path_offset(fdt, "/cpus/cpu@0"); |
| lgpl_printf("Update cpu0-supply!\n"); |
| fdt_setprop_u32(fdt, cpu0_offset, "cpu0-supply", phandle); |
| } |
| |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy8824"); |
| } |
| } |
| else { |
| /* disable SY8824 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy8824"); |
| while(offset > 0) { |
| lgpl_printf("Delete silergy,sy8824 for Kernel !\n"); |
| fdt_del_node(fdt, offset); |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy8824"); |
| } |
| } |
| |
| if (SY20276 == pmic_id) { |
| lgpl_printf("Select PMIC chip sy20276 for Kernel !\n"); |
| |
| /* enable SY20276 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy20276"); |
| while(offset > 0) { |
| lgpl_printf("Update silergy,sy20276!\n"); |
| uint32_t phandle = fdt_get_phandle(fdt, offset); |
| fdt_setprop_string(fdt, offset, "status", "okay"); |
| if((phandle != (uint32_t)(-1)) && phandle) { |
| int cpu0_offset = fdt_path_offset(fdt, "/cpus/cpu@0"); |
| lgpl_printf("Update cpu0-supply!\n"); |
| fdt_setprop_u32(fdt, cpu0_offset, "cpu0-supply", phandle); |
| } |
| |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy20276"); |
| } |
| } |
| else { |
| /* disable SY20276 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy20276"); |
| while(offset > 0) { |
| lgpl_printf("Delete silergy,sy20276 for Kernel !\n"); |
| fdt_del_node(fdt, offset); |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy20276"); |
| } |
| } |
| |
| if (SY20278 == pmic_id) { |
| lgpl_printf("Select PMIC chip sy20278 for Kernel !\n"); |
| |
| /* enable SY20278 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy20278"); |
| while(offset > 0) { |
| lgpl_printf("Update silergy,sy20278!\n"); |
| uint32_t phandle = fdt_get_phandle(fdt, offset); |
| fdt_setprop_string(fdt, offset, "status", "okay"); |
| if((phandle != (uint32_t)(-1)) && phandle) { |
| int cpu0_offset = fdt_path_offset(fdt, "/cpus/cpu@0"); |
| lgpl_printf("Update cpu0-supply!\n"); |
| fdt_setprop_u32(fdt, cpu0_offset, "cpu0-supply", phandle); |
| } |
| |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy20278"); |
| } |
| } |
| else { |
| /* disable SY20278 */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "silergy,sy20278"); |
| while(offset > 0) { |
| lgpl_printf("Delete silergy,sy20278 for Kernel !\n"); |
| fdt_del_node(fdt, offset); |
| offset = fdt_node_offset_by_compatible(fdt, offset, "silergy,sy20278"); |
| } |
| } |
| } |
| |
| void fdt_set_memory(void *fdt, unsigned int memory_size) { |
| int offset; |
| unsigned int reg[4]; |
| int memory_offset = 0x01000000; |
| |
| /* find the offset in FDT */ |
| offset = fdt_path_offset(fdt,"/memory"); |
| if (offset == -FDT_ERR_NOTFOUND){ |
| offset = fdt_add_subnode(fdt, 0, "/memory"); |
| if (offset < 0) |
| lgpl_printf("Failed to find memory in the fdt !\n"); |
| return; |
| } |
| |
| lgpl_printf("set memory 0x%x to fdt !\n", memory_size); |
| |
| reg[0] = cpu_to_fdt32(0); |
| reg[1] = cpu_to_fdt32(memory_offset); |
| reg[2] = cpu_to_fdt32(0); |
| reg[3] = cpu_to_fdt32(memory_size - memory_offset); |
| |
| fdt_setprop(fdt, offset, "reg", ®, sizeof(reg)); |
| |
| } |
| |
| void fdt_set_ramoops(void *fdt, unsigned int memory_size) { |
| int offset; |
| unsigned int reg[4]; |
| unsigned int reserved_size = 0x00100000; |
| |
| |
| /* find the offset in FDT */ |
| offset = fdt_node_offset_by_compatible(fdt, -1, "ramoops"); |
| if (offset == -FDT_ERR_NOTFOUND){ |
| lgpl_printf("Failed to find ramoops in the fdt !\n"); |
| } |
| |
| reg[0] = cpu_to_fdt32(0); |
| reg[1] = cpu_to_fdt32(memory_size - reserved_size);; |
| reg[2] = cpu_to_fdt32(0); |
| reg[3] = cpu_to_fdt32(reserved_size); |
| |
| fdt_setprop(fdt, offset, "reg", ®, sizeof(reg)); |
| } |