Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752938AbcLLPO6 (ORCPT ); Mon, 12 Dec 2016 10:14:58 -0500 Received: from smtp.codeaurora.org ([198.145.29.96]:43006 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751025AbcLLPOz (ORCPT ); Mon, 12 Dec 2016 10:14:55 -0500 DMARC-Filter: OpenDMARC Filter v1.3.1 smtp.codeaurora.org E37D1613D3 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=pass smtp.mailfrom=kimran@codeaurora.org Subject: Re: [PATCH v5] soc: qcom: Add SoC info driver To: Bjorn Andersson References: <1479302540-16690-1-git-send-email-kimran@codeaurora.org> <20161205180503.GK9322@tuxbot> Cc: andy.gross@linaro.org, lee.jones@linaro.org, David Brown , open list , "open list:ARM/QUALCOMM SUPPORT" , "open list:ARM/QUALCOMM SUPPORT" From: Imran Khan Message-ID: <70a5a48a-4b6c-f564-99e4-758a9c5d598a@codeaurora.org> Date: Mon, 12 Dec 2016 20:44:48 +0530 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.5.0 MIME-Version: 1.0 In-Reply-To: <20161205180503.GK9322@tuxbot> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 17535 Lines: 624 On 12/5/2016 11:35 PM, Bjorn Andersson wrote: > On Wed 16 Nov 05:22 PST 2016, Imran Khan wrote: > >> >> diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c >> new file mode 100644 >> index 0000000..eca46df >> --- /dev/null >> +++ b/drivers/soc/qcom/socinfo.c >> @@ -0,0 +1,989 @@ >> +/* >> + * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 and >> + * only version 2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#define PMIC_MODEL_UNKNOWN 0 >> +#define HW_PLATFORM_QRD 11 >> +#define PLATFORM_SUBTYPE_QRD_INVALID 6 >> +#define PLATFORM_SUBTYPE_INVALID 4 >> +/* >> + * SOC version type with major number in the upper 16 bits and minor >> + * number in the lower 16 bits. For example: >> + * 1.0 -> 0x00010000 >> + * 2.3 -> 0x00020003 >> + */ >> +#define SOC_VERSION_MAJOR(ver) (((ver) & 0xffff0000) >> 16) >> +#define SOC_VERSION_MINOR(ver) ((ver) & 0x0000ffff) >> +#define SOCINFO_FORMAT(x) (x) >> +#define SOCINFO_VERSION_MAJOR SOC_VERSION_MAJOR >> +#define SOCINFO_VERSION_MINOR SOC_VERSION_MINOR >> + >> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 >> +#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32 >> +#define SMEM_IMAGE_VERSION_SIZE 4096 >> +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 >> +#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20 >> +#define SMEM_IMAGE_VERSION_OEM_SIZE 32 >> +#define SMEM_IMAGE_VERSION_PARTITION_APPS 10 > > Please indent the values evenly, making this slightly easier to read. > Done. >> + >> +/* >> + * Shared memory identifiers, used to acquire handles to respective memory >> + * region. >> + */ >> +#define SMEM_IMAGE_VERSION_TABLE 469 >> + >> +/* >> + * Shared memory identifiers, used to acquire handles to respective memory >> + * region. >> + */ > > Duplicating this comment doesn't add value, please just drop this second > copy and replace the above comment with something like "SMEM item ids" > Done. >> +#define SMEM_HW_SW_BUILD_ID 137 >> + >> +/* >> + * SMEM Image table indices >> + */ > > Rather than having this based on two huge macros take a look at > DEVICE_INT_ATTR() and struct dev_ext_attribute in > include/linux/device.h. It looks like if you just put the index in a > struct and use container_of to reach that you can replace these with > direct functions. > > Also, it's perfectly fine to always specify a store operation and use > 0444 vs 0644 to control it's availability. So you don't need two sets if > you just expose the mode in your macro. > If I understood this correct, the main idea here is to use dev_ext_attribute and avoid wrapper macros. Have tried to implement this suggestion in the subsequent patch set. Please let me know if it looks okay or can be improved further. >> + >> +/* >> + * Qcom SoC types >> + */ >> +enum qcom_cpu { >> + MSM_CPU_UNKNOWN = 0, >> + MSM_CPU_8960, >> + MSM_CPU_8960AB, >> + MSM_CPU_8064, >> + MSM_CPU_8974, >> + MSM_CPU_8974PRO_AA, >> + MSM_CPU_8974PRO_AB, >> + MSM_CPU_8974PRO_AC, >> + MSM_CPU_8916, >> + MSM_CPU_8084, >> + MSM_CPU_8996 >> +}; > > These are now "unused" (per below comment). > Done. As these are no more needed. >> + >> +struct qcom_soc_info { >> + enum qcom_cpu generic_soc_type; > > This enum is no longer used, seems like you can turn cpu_of_id into a > simple char *[] array with with soc_id_string indexed by id. > True. Have implemented cpu_of_id as per this suggestion. >> + char *soc_id_string; >> +}; >> + >> + >> +static struct smem_image_version *smem_image_version; >> +static bool smem_img_tbl_avlbl = true; > > You don't need to keep track of this separately, just test if > smem_image_version is NULL or not (reset it to NULL if qcom_smem_get() > fails). > Done. >> +static bool hw_subtype_valid = true; >> +static u32 hw_subtype; > > Don't keep track of this in a global variable, it's perfectly fine to > figure it out in qcom_get_platform_subtype() > Done. >> + >> + >> +/* max socinfo format version supported */ >> +#define MAX_SOCINFO_FORMAT SOCINFO_FORMAT(12) > > It's fine to drop SOCINFO_FORMAT and just put 12 here. > Done. >> + >> +static struct qcom_soc_info cpu_of_id[] = { >> + >> + [0] = {MSM_CPU_UNKNOWN, "Unknown CPU"}, >> + >> +static u32 socinfo_format; >> + >> +static u32 socinfo_get_id(void); >> + > > Merge socinfo_init_sysfs() and qcom_socinfo_init() into one function and > you don't need to declare this here. > Done. >> +/* socinfo: sysfs functions */ >> + >> +static char *socinfo_get_id_string(void) >> +{ >> + return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL; > > socinfo can't be NULL anymore. > True. This checking is redundant indeed. In the subsequent patch set I have removed redundant checking of socinfo being NULL or not. >> +} >> + >> +static char *socinfo_get_image_version_base_address(struct device *dev) > > Inline this into qcom_socinfo_init() as described below. > Done. >> +{ >> + size_t size, size_in; >> + void *ptr; >> + >> + ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE, >> + &size); >> + if (IS_ERR(ptr)) >> + return ptr; >> + >> + size_in = SMEM_IMAGE_VERSION_SIZE; >> + if (size_in != size) { >> + dev_err(dev, "Wrong size for smem item\n"); >> + return ERR_PTR(-EINVAL); >> + } >> + >> + return ptr; >> +} >> + >> + >> +static u32 socinfo_get_version(void) >> +{ >> + return (socinfo) ? socinfo->v0_1.version : 0; > > socinfo can't be NULL, so you can just inline "socinfo->v0_1.version" in > the one place calling this. > Done. >> +} >> + >> +static char *socinfo_get_build_id(void) >> +{ >> + return (socinfo) ? socinfo->v0_1.build_id : NULL; > > socinfo can't be NULL, and this is only called from one place, so inline > "socinfo->v0_1.build_id" there. > Done. >> +} >> + >> +static u32 socinfo_get_raw_version(void) >> +{ >> + return socinfo ? > > socinfo can't be NULL. > >> + (socinfo_format >= SOCINFO_FORMAT(2) ? > As mentioned above, have taken care of this. > qcom_soc_attr_raw_version will not be created unless socinfo_format >= > 2, so this can't be called if socinfo_format < 2, so you don't need this > extra check. > >> + socinfo->v0_2.raw_version : 0) >> + : 0; > > And as this is then simplified to "return socinfo->v0_2.raw_version;" I > think you should just inline it in the one place calling this function. > Done. Have removed redundant checking of socinfo format version from this and other functions and also have made these functions as much inline as possible. >> +} >> + >> +static u32 socinfo_get_platform_type(void) >> +{ >> + return socinfo ? >> + (socinfo_format >= SOCINFO_FORMAT(3) ? >> + socinfo->v0_3.hw_platform : 0) >> + : 0; >> +} >> + >> +static ssize_t >> +qcom_get_raw_version(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) > > This is the "show" function of an attr, so please drop the "get" and add > "_show". > Done. Have renamed attr show and store functions properly. >> +{ >> + return sprintf(buf, "%u\n", >> + socinfo_get_raw_version()); > > And please inline as much as you can of these, there's little point in > having a helper function to wrap a single line accessing the element in > the socinfo struct. > Done. Have tried this but please let me know if you see any further scope in this regard. >> +} >> + >> +static ssize_t >> +qcom_get_build_id(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + return scnprintf(buf, SMEM_SOCINFO_BUILD_ID_LENGTH, "%s\n", >> + socinfo_get_build_id()); >> +} >> + >> + >> +static ssize_t >> +qcom_get_pmic_die_revision(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + return sprintf(buf, "%u\n", socinfo_get_pmic_die_revision()); >> +} >> + >> +static ssize_t >> +qcom_get_image_version(int index, char *buf) >> +{ > > Just inline these into the version show() and store() functions. And > whenever you copy to the sysfs buffer, it's of size PAGE_SIZE. > Done. >> + return scnprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%s\n", >> + smem_image_version[index].name); >> +} >> + >> +static ssize_t >> +qcom_set_image_crm_version(int index, const char *buf) >> +{ >> + return strlcpy(smem_image_version[index].oem, buf, >> + SMEM_IMAGE_VERSION_OEM_SIZE); >> +} >> + >> +static struct device_attribute qcom_soc_attr_raw_version = >> + __ATTR(raw_version, 0444, qcom_get_raw_version, NULL); > > Rather than writing all these out here you can put them in an array of: > > struct qcom_socinfo_attr { > struct device_attribute attr; > int min_version; > }; > > #define QCOM_SOCINFO_ATTR(_name, _show, _min_version) \ > { __ATTR(_name, 0444, _show, NULL), _min_version) } > > static const struct qcom_socinfo_attr qcom_socinfo_attrs[] = { > QCOM_SOCINFO_ATTR(raw_version, qcom_get_raw_version, 2), > }; > > And then socinfo_populate_sysfs_files() will be a matter of looping over > this array and comparing min_version and registering the attr. > Done. This makes the implementation really neat and concise. Thanks for this suggestion. >> + >> +static struct device_attribute qcom_soc_attr_vendor = >> + __ATTR(vendor, 0444, qcom_get_vendor, NULL); >> + >> +static struct device_attribute qcom_soc_attr_build_id = >> + __ATTR(build_id, 0444, qcom_get_build_id, NULL); >> + /* >> + * Expose SMEM_IMAGE_TABLE to sysfs only when we have IMAGE_TABLE >> + * available in SMEM. As IMAGE_TABLE and SOCINFO are two separate >> + * items within SMEM, we expose the remaining soc information(i.e >> + * only the SOCINFO item available in SMEM) to sysfs even in the >> + * absence of an IMAGE_TABLE. >> + */ >> + if (smem_img_tbl_avlbl) { >> + for (img_idx = 0; img_idx < SMEM_IMAGE_VERSION_BLOCKS_COUNT; >> + img_idx++) { >> + if (smem_image_table[img_idx]) { >> + err = sysfs_create_group(&qcom_soc_device->kobj, >> + smem_image_table[img_idx]); >> + if (err) >> + break; > > Should we just ignore this error? > Yeah. I think we can and should ignore this error. Failing to create sysfs entry for one item in image table should not deprive other SMEM image items. >> + } >> + } >> + } >> + err = device_create_file(qcom_soc_device, &qcom_soc_attr_vendor); >> + if (!err) { > > Flip this conditional around and handle the error here, it saves you one > indentation level for the rest of this function. > Done. No more needed in new implementation. >> + switch (socinfo_format) { >> + case SOCINFO_FORMAT(12): >> + err = device_create_file(qcom_soc_device, >> + &qcom_soc_attr_chip_family); >> + if (!err) >> + err = device_create_file(qcom_soc_device, >> + &qcom_soc_attr_raw_device_family); >> + if (!err) >> + err = device_create_file(qcom_soc_device, >> + &qcom_soc_attr_raw_device_number); >> + return 0; >> + default: >> + dev_err(qcom_soc_device, "Unknown socinfo format: v0.%u\n", >> + SOCINFO_VERSION_MINOR(socinfo_format)); >> + return -EINVAL; >> + } >> + } else { >> + dev_err(qcom_soc_device, "Could not create sysfs entry\n"); >> + return err; >> + } >> +} >> + >> +static int socinfo_init_sysfs(struct device *dev) > > This device * is specifically the smem device, so if you name it > "smem_dev"... > Done. >> +{ >> + struct device *qcom_soc_device; > > ...then you can name this "dev" to shorten things. > Done. >> + struct soc_device *soc_dev; >> + struct soc_device_attribute *soc_dev_attr; > > "attr" would be long enough. > Done. >> + u32 soc_version; >> + >> + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); >> + if (!soc_dev_attr) >> + return -ENOMEM; >> + >> + soc_version = socinfo_get_version(); >> + >> + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", socinfo_get_id()); >> + soc_dev_attr->family = "Snapdragon"; >> + soc_dev_attr->machine = socinfo_get_id_string(); > > Single use of socinfo_get_id_string() so you should just go: > > unsigned int id = socinfo->v0_1.id; > > and then use that for your soc_id print and use soc_id_string[id] here. > Done. >> + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u", >> + SOC_VERSION_MAJOR(soc_version), >> + SOC_VERSION_MINOR(soc_version)); >> + >> + soc_dev = soc_device_register(soc_dev_attr); >> + if (IS_ERR(soc_dev)) { >> + kfree(soc_dev_attr); >> + return PTR_ERR(soc_dev); >> + } >> + >> + qcom_soc_device = soc_device_to_device(soc_dev); >> + socinfo_populate_sysfs_files(qcom_soc_device); >> + return 0; >> +} >> + >> +static u32 socinfo_get_id(void) >> +{ >> + return (socinfo) ? socinfo->v0_1.id : 0; > > As we no longer expose the socinfo functions to external callers this > will only ever be called if socinfo_init() succeeded, so no need to > check for socinfo being NULL. > Done. >> +} >> + >> +static int socinfo_select_format(struct device *dev) >> +{ >> + u32 f_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.format); >> + u32 f_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.format); >> + >> + if (f_maj != 0) { >> + dev_err(dev, "Unsupported format v%u.%u.\n", >> + f_maj, f_min); >> + return -EINVAL; >> + } >> + >> + if (socinfo->v0_1.format > MAX_SOCINFO_FORMAT) { >> + dev_warn(dev, "Unsupported format v%u.%u. Falling back to v%u.%u.\n", >> + f_maj, f_min, SOCINFO_VERSION_MAJOR(MAX_SOCINFO_FORMAT), >> + SOCINFO_VERSION_MINOR(MAX_SOCINFO_FORMAT)); >> + socinfo_format = MAX_SOCINFO_FORMAT; > > Don't fall back, just print an error and fail. > Done. >> + } else { >> + socinfo_format = socinfo->v0_1.format; >> + } >> + return 0; >> +} >> + >> +int qcom_socinfo_init(struct platform_device *pdev) > > As smem doesn't care if we're failing here you can just have the return > type void. > Done. >> +{ >> + size_t size; >> + >> + socinfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, >> + &size); >> + if (IS_ERR(socinfo) || (socinfo_select_format(&pdev->dev) < 0)) { >> + dev_warn(&pdev->dev, >> + "Coudn't find soc information; Unable to setup socinfo.\n"); >> + return -ENOMEM; > > "Couldn't find socinfo" and return -ENOENT if IS_ERR(socinfo). > Done >> + } > > And then just move the rest of socinfo_select_format() here: > > if (socinfo->v0.1.format > MAX_SOCINFO_FORMAT) { > dev_err(&pdev->dev, "Invalid socinfo version %d.%d\n", > MAJOR(), MINOR()); > return -EINVAL; > } > Done >> + >> + if (!socinfo_get_id()) { >> + dev_err(&pdev->dev, "socinfo: Unknown SoC ID!\n"); >> + return -EINVAL; >> + } >> + >> + WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), >> + "New IDs added! ID => CPU mapping needs an update.\n"); >> + >> + smem_image_version = (struct smem_image_version *) >> + socinfo_get_image_version_base_address(&pdev->dev); > > You could make socinfo_get_image_version_base_address() return void * > and you don't have to do the type cast. But you can make this more > compact by just inlining the qcom_smem_get() here directly. > > smem_image_version = qcom_smem_get(QCOM_SMEM_HOST_ANY, > SMEM_IMAGE_VERSION_TABLE, > &size); > if (IS_ERR(smem_image_version) || size != SMEM_IMAGE_VERSION_SIZE) { > ... > } > Done >> + if (IS_ERR(smem_image_version)) { >> + dev_warn(&pdev->dev, "Unable to get address for image version table.\n"); > > "Image version table missing" should keep you < 80 characters. > Done. >> + smem_img_tbl_avlbl = false; >> + } >> + >> + hw_subtype = socinfo_get_platform_subtype(); >> + if (socinfo_get_platform_type() == HW_PLATFORM_QRD) { >> + if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) { >> + dev_err(&pdev->dev, "Invalid hardware platform sub type for qrd found\n"); >> + hw_subtype_valid = false; >> + } >> + } else { >> + if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { >> + dev_err(&pdev->dev, "Invalid hardware platform subtype\n"); >> + hw_subtype_valid = false; >> + } >> + } > > This will only matter if format >= 6 and hw_subtype is outside the array > of strings, I think you should just be optimistic about this not being > an issue and then in all cases where you dereference a lookup table > check that you're within bounds, or fail with -EINVAL; to userspace. > True. Have taken care of this. No more tracking this with a global variable. >> + >> + socinfo_init_sysfs(&pdev->dev); > > socinfo_init_sysfs() is just the second half of this function, so inline > it here. > Done. now this is inline. >> + >> + /* Feed the soc specific unique data into entropy pool */ >> + add_device_randomness(socinfo, size); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL(qcom_socinfo_init); >> + > > No empty lines at the end of the file, please. > Done. > Regards, > Bjorn > Thanks Bjorn for review comments. Regards, Imran -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation