cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qcom-coincell.c (3721B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
      3 * Copyright (c) 2015, Sony Mobile Communications Inc.
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/module.h>
      8#include <linux/slab.h>
      9#include <linux/of.h>
     10#include <linux/regmap.h>
     11#include <linux/of_device.h>
     12#include <linux/platform_device.h>
     13
     14struct qcom_coincell {
     15	struct device	*dev;
     16	struct regmap	*regmap;
     17	u32		base_addr;
     18};
     19
     20#define QCOM_COINCELL_REG_RSET		0x44
     21#define QCOM_COINCELL_REG_VSET		0x45
     22#define QCOM_COINCELL_REG_ENABLE	0x46
     23
     24#define QCOM_COINCELL_ENABLE		BIT(7)
     25
     26static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 };
     27static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 };
     28/* NOTE: for pm8921 and others, voltage of 2500 is 16 (10000b), not 0 */
     29
     30/* if enable==0, rset and vset are ignored */
     31static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset,
     32				     int vset, bool enable)
     33{
     34	int i, j, rc;
     35
     36	/* if disabling, just do that and skip other operations */
     37	if (!enable)
     38		return regmap_write(chgr->regmap,
     39			  chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0);
     40
     41	/* find index for current-limiting resistor */
     42	for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++)
     43		if (rset == qcom_rset_map[i])
     44			break;
     45
     46	if (i >= ARRAY_SIZE(qcom_rset_map)) {
     47		dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset);
     48		return -EINVAL;
     49	}
     50
     51	/* find index for charge voltage */
     52	for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++)
     53		if (vset == qcom_vset_map[j])
     54			break;
     55
     56	if (j >= ARRAY_SIZE(qcom_vset_map)) {
     57		dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset);
     58		return -EINVAL;
     59	}
     60
     61	rc = regmap_write(chgr->regmap,
     62			  chgr->base_addr + QCOM_COINCELL_REG_RSET, i);
     63	if (rc) {
     64		/*
     65		 * This is mainly to flag a bad base_addr (reg) from dts.
     66		 * Other failures writing to the registers should be
     67		 * extremely rare, or indicative of problems that
     68		 * should be reported elsewhere (eg. spmi failure).
     69		 */
     70		dev_err(chgr->dev, "could not write to RSET register\n");
     71		return rc;
     72	}
     73
     74	rc = regmap_write(chgr->regmap,
     75		chgr->base_addr + QCOM_COINCELL_REG_VSET, j);
     76	if (rc)
     77		return rc;
     78
     79	/* set 'enable' register */
     80	return regmap_write(chgr->regmap,
     81			    chgr->base_addr + QCOM_COINCELL_REG_ENABLE,
     82			    QCOM_COINCELL_ENABLE);
     83}
     84
     85static int qcom_coincell_probe(struct platform_device *pdev)
     86{
     87	struct device_node *node = pdev->dev.of_node;
     88	struct qcom_coincell chgr;
     89	u32 rset = 0;
     90	u32 vset = 0;
     91	bool enable;
     92	int rc;
     93
     94	chgr.dev = &pdev->dev;
     95
     96	chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL);
     97	if (!chgr.regmap) {
     98		dev_err(chgr.dev, "Unable to get regmap\n");
     99		return -EINVAL;
    100	}
    101
    102	rc = of_property_read_u32(node, "reg", &chgr.base_addr);
    103	if (rc)
    104		return rc;
    105
    106	enable = !of_property_read_bool(node, "qcom,charger-disable");
    107
    108	if (enable) {
    109		rc = of_property_read_u32(node, "qcom,rset-ohms", &rset);
    110		if (rc) {
    111			dev_err(chgr.dev,
    112				"can't find 'qcom,rset-ohms' in DT block");
    113			return rc;
    114		}
    115
    116		rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset);
    117		if (rc) {
    118			dev_err(chgr.dev,
    119			    "can't find 'qcom,vset-millivolts' in DT block");
    120			return rc;
    121		}
    122	}
    123
    124	return qcom_coincell_chgr_config(&chgr, rset, vset, enable);
    125}
    126
    127static const struct of_device_id qcom_coincell_match_table[] = {
    128	{ .compatible = "qcom,pm8941-coincell", },
    129	{}
    130};
    131
    132MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
    133
    134static struct platform_driver qcom_coincell_driver = {
    135	.driver	= {
    136		.name		= "qcom-spmi-coincell",
    137		.of_match_table	= qcom_coincell_match_table,
    138	},
    139	.probe		= qcom_coincell_probe,
    140};
    141
    142module_platform_driver(qcom_coincell_driver);
    143
    144MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver");
    145MODULE_LICENSE("GPL v2");