diff --git a/MAINTAINERS b/MAINTAINERS index 9fc289112faf..80a4fac5f346 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22691,6 +22691,14 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst F: drivers/net/ethernet/qualcomm/rmnet/ F: include/linux/if_rmnet.h +QUALCOMM SPMI CONTROLLER (PMIC ARBITER) DRIVER +M: Abel Vesa +M: David Collins +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spmi/qcom,*spmi-pmic-arb*.yaml +F: drivers/spmi/spmi-pmic-arb.c + QUALCOMM TEE (QCOMTEE) DRIVER M: Amirreza Zarrabi L: linux-arm-msm@vger.kernel.org @@ -25918,6 +25926,7 @@ F: tools/spi/ SPMI SUBSYSTEM M: Stephen Boyd +M: Abel Vesa L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c index 0ec3a0c4b1de..196525ba0b4b 100644 --- a/drivers/iio/adc/qcom-spmi-iadc.c +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -16,6 +16,7 @@ #include #include #include +#include /* IADC register and bit definition */ #define IADC_REVISION2 0x1 @@ -94,7 +95,6 @@ * struct iadc_chip - IADC Current ADC device structure. * @regmap: regmap for register read/write. * @dev: This device pointer. - * @base: base offset for the ADC peripheral. * @rsense: Values of the internal and external sense resister in micro Ohms. * @poll_eoc: Poll for end of conversion instead of waiting for IRQ. * @offset: Raw offset values for the internal and external channels. @@ -105,7 +105,6 @@ struct iadc_chip { struct regmap *regmap; struct device *dev; - u16 base; bool poll_eoc; u32 rsense[2]; u16 offset[2]; @@ -114,77 +113,59 @@ struct iadc_chip { struct completion complete; }; -static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data) -{ - unsigned int val; - int ret; - - ret = regmap_read(iadc->regmap, iadc->base + offset, &val); - if (ret < 0) - return ret; - - *data = val; - return 0; -} - -static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data) -{ - return regmap_write(iadc->regmap, iadc->base + offset, data); -} - static int iadc_reset(struct iadc_chip *iadc) { - u8 data; + u32 data; int ret; - ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + ret = regmap_write(iadc->regmap, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); if (ret < 0) return ret; - ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data); + ret = regmap_read(iadc->regmap, IADC_PERH_RESET_CTL3, &data); if (ret < 0) return ret; - ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + ret = regmap_write(iadc->regmap, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); if (ret < 0) return ret; data |= IADC_FOLLOW_WARM_RB; - return iadc_write(iadc, IADC_PERH_RESET_CTL3, data); + return regmap_write(iadc->regmap, IADC_PERH_RESET_CTL3, data); } static int iadc_set_state(struct iadc_chip *iadc, bool state) { - return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0); + return regmap_write(iadc->regmap, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0); } static void iadc_status_show(struct iadc_chip *iadc) { - u8 mode, sta1, chan, dig, en, req; + u32 mode, sta1, chan, dig, en, req; int ret; - ret = iadc_read(iadc, IADC_MODE_CTL, &mode); + ret = regmap_read(iadc->regmap, IADC_MODE_CTL, &mode); if (ret < 0) return; - ret = iadc_read(iadc, IADC_DIG_PARAM, &dig); + ret = regmap_read(iadc->regmap, IADC_DIG_PARAM, &dig); if (ret < 0) return; - ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan); + ret = regmap_read(iadc->regmap, IADC_CH_SEL_CTL, &chan); if (ret < 0) return; - ret = iadc_read(iadc, IADC_CONV_REQ, &req); + ret = regmap_read(iadc->regmap, IADC_CONV_REQ, &req); if (ret < 0) return; - ret = iadc_read(iadc, IADC_STATUS1, &sta1); + ret = regmap_read(iadc->regmap, IADC_STATUS1, &sta1); if (ret < 0) return; - ret = iadc_read(iadc, IADC_EN_CTL1, &en); + ret = regmap_read(iadc->regmap, IADC_EN_CTL1, &en); if (ret < 0) return; @@ -200,34 +181,34 @@ static int iadc_configure(struct iadc_chip *iadc, int channel) /* Mode selection */ mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN; - ret = iadc_write(iadc, IADC_MODE_CTL, mode); + ret = regmap_write(iadc->regmap, IADC_MODE_CTL, mode); if (ret < 0) return ret; /* Channel selection */ - ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel); + ret = regmap_write(iadc->regmap, IADC_CH_SEL_CTL, channel); if (ret < 0) return ret; /* Digital parameter setup */ decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT; - ret = iadc_write(iadc, IADC_DIG_PARAM, decim); + ret = regmap_write(iadc->regmap, IADC_DIG_PARAM, decim); if (ret < 0) return ret; /* HW settle time delay */ - ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME); + ret = regmap_write(iadc->regmap, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME); if (ret < 0) return ret; - ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES); + ret = regmap_write(iadc->regmap, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES); if (ret < 0) return ret; if (IADC_DEF_AVG_SAMPLES) - ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET); + ret = regmap_write(iadc->regmap, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET); else - ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0); + ret = regmap_write(iadc->regmap, IADC_FAST_AVG_EN, 0); if (ret < 0) return ret; @@ -240,19 +221,19 @@ static int iadc_configure(struct iadc_chip *iadc, int channel) return ret; /* Request conversion */ - return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET); + return regmap_write(iadc->regmap, IADC_CONV_REQ, IADC_CONV_REQ_SET); } static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us) { unsigned int count, retry; + u32 sta1; int ret; - u8 sta1; retry = interval_us / IADC_CONV_TIME_MIN_US; for (count = 0; count < retry; count++) { - ret = iadc_read(iadc, IADC_STATUS1, &sta1); + ret = regmap_read(iadc->regmap, IADC_STATUS1, &sta1); if (ret < 0) return ret; @@ -268,11 +249,6 @@ static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us) return -ETIMEDOUT; } -static int iadc_read_result(struct iadc_chip *iadc, u16 *data) -{ - return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2); -} - static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data) { unsigned int wait; @@ -297,7 +273,7 @@ static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data) } if (!ret) - ret = iadc_read_result(iadc, data); + ret = regmap_bulk_read(iadc->regmap, IADC_DATA, data, sizeof(*data)); exit: iadc_set_state(iadc, false); if (ret < 0) @@ -393,33 +369,33 @@ static int iadc_update_offset(struct iadc_chip *iadc) static int iadc_version_check(struct iadc_chip *iadc) { - u8 val; + u32 val; int ret; - ret = iadc_read(iadc, IADC_PERPH_TYPE, &val); + ret = regmap_read(iadc->regmap, IADC_PERPH_TYPE, &val); if (ret < 0) return ret; if (val < IADC_PERPH_TYPE_ADC) { - dev_err(iadc->dev, "%d is not ADC\n", val); + dev_err(iadc->dev, "%u is not ADC\n", val); return -EINVAL; } - ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val); + ret = regmap_read(iadc->regmap, IADC_PERPH_SUBTYPE, &val); if (ret < 0) return ret; if (val < IADC_PERPH_SUBTYPE_IADC) { - dev_err(iadc->dev, "%d is not IADC\n", val); + dev_err(iadc->dev, "%u is not IADC\n", val); return -EINVAL; } - ret = iadc_read(iadc, IADC_REVISION2, &val); + ret = regmap_read(iadc->regmap, IADC_REVISION2, &val); if (ret < 0) return ret; if (val < IADC_REVISION2_SUPPORTED_IADC) { - dev_err(iadc->dev, "revision %d not supported\n", val); + dev_err(iadc->dev, "revision %u not supported\n", val); return -EINVAL; } @@ -429,7 +405,7 @@ static int iadc_version_check(struct iadc_chip *iadc) static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node) { int ret, sign, int_sense; - u8 deviation; + u32 deviation; ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms", &iadc->rsense[IADC_EXT_RSENSE]); @@ -441,7 +417,7 @@ static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node) return -EINVAL; } - ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation); + ret = regmap_read(iadc->regmap, IADC_NOMINAL_RSENSE, &deviation); if (ret < 0) return ret; @@ -488,12 +464,19 @@ static void iadc_disable_irq_wake(void *data) static int iadc_probe(struct platform_device *pdev) { + struct regmap_config iadc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xff, + .fast_io = true, + }; struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; + struct spmi_subdevice *sub_sdev; + struct spmi_device *sparent; struct iio_dev *indio_dev; struct iadc_chip *iadc; int ret, irq_eoc; - u32 res; indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc)); if (!indio_dev) @@ -502,18 +485,24 @@ static int iadc_probe(struct platform_device *pdev) iadc = iio_priv(indio_dev); iadc->dev = dev; - iadc->regmap = dev_get_regmap(dev->parent, NULL); - if (!iadc->regmap) + sparent = spmi_get_parent_spmi_device(dev); + if (!sparent) return -ENODEV; + sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent); + if (IS_ERR(sub_sdev)) + return PTR_ERR(sub_sdev); + init_completion(&iadc->complete); mutex_init(&iadc->lock); - ret = of_property_read_u32(node, "reg", &res); + ret = device_property_read_u32(dev, "reg", &iadc_regmap_config.reg_base); if (ret < 0) return -ENODEV; - iadc->base = res; + iadc->regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, &iadc_regmap_config); + if (IS_ERR(iadc->regmap)) + return PTR_ERR(iadc->regmap); ret = iadc_version_check(iadc); if (ret < 0) @@ -596,3 +585,4 @@ MODULE_ALIAS("platform:qcom-spmi-iadc"); MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ivan T. Ivanov "); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index f45b5ffa4344..c40e89cc5724 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -256,6 +256,8 @@ config HP_ILO config QCOM_COINCELL tristate "Qualcomm coincell charger support" depends on MFD_SPMI_PMIC || COMPILE_TEST + depends on SPMI + select REGMAP_SPMI help This driver supports the coincell block found inside of Qualcomm PMICs. The coincell charger provides a means to diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c index b58bd34fc471..8da3736652c3 100644 --- a/drivers/misc/qcom-coincell.c +++ b/drivers/misc/qcom-coincell.c @@ -9,11 +9,11 @@ #include #include #include +#include struct qcom_coincell { struct device *dev; struct regmap *regmap; - u32 base_addr; }; #define QCOM_COINCELL_REG_RSET 0x44 @@ -35,7 +35,7 @@ static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset, /* if disabling, just do that and skip other operations */ if (!enable) return regmap_write(chgr->regmap, - chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0); + QCOM_COINCELL_REG_ENABLE, 0); /* find index for current-limiting resistor */ for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++) @@ -58,7 +58,7 @@ static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset, } rc = regmap_write(chgr->regmap, - chgr->base_addr + QCOM_COINCELL_REG_RSET, i); + QCOM_COINCELL_REG_RSET, i); if (rc) { /* * This is mainly to flag a bad base_addr (reg) from dts. @@ -71,19 +71,28 @@ static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset, } rc = regmap_write(chgr->regmap, - chgr->base_addr + QCOM_COINCELL_REG_VSET, j); + QCOM_COINCELL_REG_VSET, j); if (rc) return rc; /* set 'enable' register */ return regmap_write(chgr->regmap, - chgr->base_addr + QCOM_COINCELL_REG_ENABLE, + QCOM_COINCELL_REG_ENABLE, QCOM_COINCELL_ENABLE); } static int qcom_coincell_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; + struct regmap_config qcom_coincell_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xff, + .fast_io = true, + }; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct spmi_subdevice *sub_sdev; + struct spmi_device *sparent; struct qcom_coincell chgr; u32 rset = 0; u32 vset = 0; @@ -92,16 +101,25 @@ static int qcom_coincell_probe(struct platform_device *pdev) chgr.dev = &pdev->dev; - chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!chgr.regmap) { - dev_err(chgr.dev, "Unable to get regmap\n"); - return -EINVAL; - } - - rc = of_property_read_u32(node, "reg", &chgr.base_addr); + rc = device_property_read_u32(dev, "reg", &qcom_coincell_regmap_config.reg_base); if (rc) return rc; + sparent = spmi_get_parent_spmi_device(dev); + if (!sparent) + return -ENODEV; + + sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent); + if (IS_ERR(sub_sdev)) + return PTR_ERR(sub_sdev); + + chgr.regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, + &qcom_coincell_regmap_config); + if (IS_ERR(chgr.regmap)) { + dev_err(chgr.dev, "Unable to get regmap\n"); + return PTR_ERR(chgr.regmap); + } + enable = !of_property_read_bool(node, "qcom,charger-disable"); if (enable) { @@ -142,3 +160,4 @@ module_platform_driver(qcom_coincell_driver); MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 083a5e528960..d2f5bb236663 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -479,6 +479,7 @@ config NVMEM_SNVS_LPGPR config NVMEM_SPMI_SDAM tristate "SPMI SDAM Support" depends on SPMI + select REGMAP_SPMI help This driver supports the Shared Direct Access Memory Module on Qualcomm Technologies, Inc. PMICs. It provides the clients diff --git a/drivers/nvmem/qcom-spmi-sdam.c b/drivers/nvmem/qcom-spmi-sdam.c index 4f1cca6eab71..2aec95dff05d 100644 --- a/drivers/nvmem/qcom-spmi-sdam.c +++ b/drivers/nvmem/qcom-spmi-sdam.c @@ -9,6 +9,7 @@ #include #include #include +#include #define SDAM_MEM_START 0x40 #define REGISTER_MAP_ID 0x40 @@ -20,7 +21,6 @@ struct sdam_chip { struct regmap *regmap; struct nvmem_config sdam_config; - unsigned int base; unsigned int size; }; @@ -73,7 +73,7 @@ static int sdam_read(void *priv, unsigned int offset, void *val, return -EINVAL; } - rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes); + rc = regmap_bulk_read(sdam->regmap, offset, val, bytes); if (rc < 0) dev_err(dev, "Failed to read SDAM offset %#x len=%zd, rc=%d\n", offset, bytes, rc); @@ -100,7 +100,7 @@ static int sdam_write(void *priv, unsigned int offset, void *val, return -EINVAL; } - rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes); + rc = regmap_bulk_write(sdam->regmap, offset, val, bytes); if (rc < 0) dev_err(dev, "Failed to write SDAM offset %#x len=%zd, rc=%d\n", offset, bytes, rc); @@ -110,8 +110,17 @@ static int sdam_write(void *priv, unsigned int offset, void *val, static int sdam_probe(struct platform_device *pdev) { + struct regmap_config sdam_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xff, + .fast_io = true, + }; struct sdam_chip *sdam; struct nvmem_device *nvmem; + struct spmi_device *sparent; + struct spmi_subdevice *sub_sdev; + struct device *dev = &pdev->dev; unsigned int val; int rc; @@ -119,19 +128,26 @@ static int sdam_probe(struct platform_device *pdev) if (!sdam) return -ENOMEM; - sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!sdam->regmap) { - dev_err(&pdev->dev, "Failed to get regmap handle\n"); - return -ENXIO; - } + sparent = spmi_get_parent_spmi_device(dev); + if (!sparent) + return -ENODEV; - rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base); + sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent); + if (IS_ERR(sub_sdev)) + return PTR_ERR(sub_sdev); + + rc = device_property_read_u32(dev, "reg", &sdam_regmap_config.reg_base); if (rc < 0) { - dev_err(&pdev->dev, "Failed to get SDAM base, rc=%d\n", rc); + dev_err(dev, "Failed to get SDAM base, rc=%d\n", rc); return -EINVAL; } - rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val); + sdam->regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, &sdam_regmap_config); + if (IS_ERR(sdam->regmap)) + return dev_err_probe(dev, PTR_ERR(sdam->regmap), + "Failed to get regmap handle\n"); + + rc = regmap_read(sdam->regmap, SDAM_SIZE, &val); if (rc < 0) { dev_err(&pdev->dev, "Failed to read SDAM_SIZE rc=%d\n", rc); return -EINVAL; @@ -159,7 +175,7 @@ static int sdam_probe(struct platform_device *pdev) } dev_dbg(&pdev->dev, "SDAM base=%#x size=%u registered successfully\n", - sdam->base, sdam->size); + sdam_regmap_config.reg_base, sdam->size); return 0; } @@ -181,3 +197,4 @@ module_platform_driver(sdam_driver); MODULE_DESCRIPTION("QCOM SPMI SDAM driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index d910a5d1a1ac..c5e260a4c7f8 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -138,7 +138,9 @@ config PHY_QCOM_QUSB2 config PHY_QCOM_EUSB2_REPEATER tristate "Qualcomm PMIC eUSB2 Repeater Driver" depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on SPMI select GENERIC_PHY + select REGMAP_SPMI help Enable support for the USB high-speed eUSB2 repeater on Qualcomm PMICs. The repeater is paired with a Synopsys or M31 eUSB2 Phy diff --git a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c index efeec4709a15..89bc1c89fc85 100644 --- a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c +++ b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c @@ -9,6 +9,7 @@ #include #include #include +#include /* eUSB2 status registers */ #define EUSB2_RPTR_STATUS 0x08 @@ -66,7 +67,6 @@ struct eusb2_repeater { struct phy *phy; struct regulator_bulk_data *vregs; const struct eusb2_repeater_cfg *cfg; - u32 base; enum phy_mode mode; }; @@ -143,7 +143,6 @@ static int eusb2_repeater_init(struct phy *phy) struct eusb2_repeater *rptr = phy_get_drvdata(phy); struct device_node *np = rptr->dev->of_node; struct regmap *regmap = rptr->regmap; - u32 base = rptr->base; u32 poll_val; s32 dt_val; int ret; @@ -154,37 +153,37 @@ static int eusb2_repeater_init(struct phy *phy) if (ret) return ret; - regmap_write(regmap, base + EUSB2_EN_CTL1, EUSB2_RPTR_EN); + regmap_write(regmap, EUSB2_EN_CTL1, EUSB2_RPTR_EN); /* Write registers from init table */ for (int i = 0; i < rptr->cfg->init_tbl_num; i++) - regmap_write(regmap, base + rptr->cfg->init_tbl[i].reg, + regmap_write(regmap, rptr->cfg->init_tbl[i].reg, rptr->cfg->init_tbl[i].value); /* Override registers from devicetree values */ if (!of_property_read_u8(np, "qcom,tune-usb2-preem", &val)) - regmap_write(regmap, base + EUSB2_TUNE_USB2_PREEM, val); + regmap_write(regmap, EUSB2_TUNE_USB2_PREEM, val); if (!of_property_read_u8(np, "qcom,tune-usb2-disc-thres", &val)) - regmap_write(regmap, base + EUSB2_TUNE_HSDISC, val); + regmap_write(regmap, EUSB2_TUNE_HSDISC, val); if (!of_property_read_u8(np, "qcom,tune-usb2-amplitude", &val)) - regmap_write(regmap, base + EUSB2_TUNE_IUSB2, val); + regmap_write(regmap, EUSB2_TUNE_IUSB2, val); if (!of_property_read_u8(np, "qcom,tune-res-fsdif", &val)) - regmap_write(regmap, base + EUSB2_TUNE_RES_FSDIF, val); + regmap_write(regmap, EUSB2_TUNE_RES_FSDIF, val); if (!of_property_read_s32(np, "qcom,squelch-detector-bp", &dt_val)) { for (i = 0; i < ARRAY_SIZE(squelch_detector); i++) { if (squelch_detector[i] == dt_val) { - regmap_write(regmap, base + EUSB2_TUNE_SQUELCH_U, i); + regmap_write(regmap, EUSB2_TUNE_SQUELCH_U, i); break; } } } /* Wait for status OK */ - ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, poll_val, + ret = regmap_read_poll_timeout(regmap, EUSB2_RPTR_STATUS, poll_val, poll_val & RPTR_OK, 10, 5); if (ret) dev_err(rptr->dev, "initialization timed-out\n"); @@ -197,7 +196,6 @@ static int eusb2_repeater_set_mode(struct phy *phy, { struct eusb2_repeater *rptr = phy_get_drvdata(phy); struct regmap *regmap = rptr->regmap; - u32 base = rptr->base; switch (mode) { case PHY_MODE_USB_HOST: @@ -206,8 +204,8 @@ static int eusb2_repeater_set_mode(struct phy *phy, * per eUSB 1.2 Spec. Below implement software workaround until * PHY and controller is fixing seen observation. */ - regmap_write(regmap, base + EUSB2_FORCE_EN_5, F_CLK_19P2M_EN); - regmap_write(regmap, base + EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN); + regmap_write(regmap, EUSB2_FORCE_EN_5, F_CLK_19P2M_EN); + regmap_write(regmap, EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN); break; case PHY_MODE_USB_DEVICE: /* @@ -216,8 +214,8 @@ static int eusb2_repeater_set_mode(struct phy *phy, * repeater doesn't clear previous value due to shared * regulators (say host <-> device mode switch). */ - regmap_write(regmap, base + EUSB2_FORCE_EN_5, 0); - regmap_write(regmap, base + EUSB2_FORCE_VAL_5, 0); + regmap_write(regmap, EUSB2_FORCE_EN_5, 0); + regmap_write(regmap, EUSB2_FORCE_VAL_5, 0); break; default: return -EINVAL; @@ -242,11 +240,18 @@ static const struct phy_ops eusb2_repeater_ops = { static int eusb2_repeater_probe(struct platform_device *pdev) { + struct regmap_config eusb2_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xff, + .fast_io = true, + }; + struct spmi_device *sparent; struct eusb2_repeater *rptr; + struct spmi_subdevice *sub_sdev; struct device *dev = &pdev->dev; struct phy_provider *phy_provider; struct device_node *np = dev->of_node; - u32 res; int ret; rptr = devm_kzalloc(dev, sizeof(*rptr), GFP_KERNEL); @@ -260,15 +265,21 @@ static int eusb2_repeater_probe(struct platform_device *pdev) if (!rptr->cfg) return -EINVAL; - rptr->regmap = dev_get_regmap(dev->parent, NULL); - if (!rptr->regmap) + sparent = spmi_get_parent_spmi_device(dev); + if (!sparent) return -ENODEV; - ret = of_property_read_u32(np, "reg", &res); + sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent); + if (IS_ERR(sub_sdev)) + return PTR_ERR(sub_sdev); + + ret = device_property_read_u32(dev, "reg", &eusb2_regmap_config.reg_base); if (ret < 0) return ret; - rptr->base = res; + rptr->regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, &eusb2_regmap_config); + if (IS_ERR(rptr->regmap)) + return PTR_ERR(rptr->regmap); ret = eusb2_repeater_init_vregs(rptr); if (ret < 0) { @@ -335,3 +346,4 @@ module_platform_driver(eusb2_repeater_driver); MODULE_DESCRIPTION("Qualcomm PMIC eUSB2 Repeater driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/power/reset/qcom-pon.c b/drivers/power/reset/qcom-pon.c index 7e108982a582..cd7ba680b841 100644 --- a/drivers/power/reset/qcom-pon.c +++ b/drivers/power/reset/qcom-pon.c @@ -11,6 +11,7 @@ #include #include #include +#include #define PON_SOFT_RB_SPARE 0x8f @@ -22,7 +23,6 @@ struct qcom_pon { struct device *dev; struct regmap *regmap; - u32 baseaddr; struct reboot_mode_driver reboot_mode; long reason_shift; }; @@ -35,7 +35,7 @@ static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot, int ret; ret = regmap_update_bits(pon->regmap, - pon->baseaddr + PON_SOFT_RB_SPARE, + PON_SOFT_RB_SPARE, GENMASK(7, pon->reason_shift), magic << pon->reason_shift); if (ret < 0) @@ -46,6 +46,15 @@ static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot, static int qcom_pon_probe(struct platform_device *pdev) { + struct regmap_config qcom_pon_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xff, + .fast_io = true, + }; + struct device *dev = &pdev->dev; + struct spmi_subdevice *sub_sdev; + struct spmi_device *sparent; struct qcom_pon *pon; long reason_shift; int error; @@ -56,17 +65,22 @@ static int qcom_pon_probe(struct platform_device *pdev) pon->dev = &pdev->dev; - pon->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!pon->regmap) { - dev_err(&pdev->dev, "failed to locate regmap\n"); + sparent = spmi_get_parent_spmi_device(dev); + if (!sparent) return -ENODEV; - } - error = of_property_read_u32(pdev->dev.of_node, "reg", - &pon->baseaddr); + sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent); + if (IS_ERR(sub_sdev)) + return PTR_ERR(sub_sdev); + + error = device_property_read_u32(dev, "reg", &qcom_pon_regmap_config.reg_base); if (error) return error; + pon->regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, &qcom_pon_regmap_config); + if (IS_ERR(pon->regmap)) + return PTR_ERR(pon->regmap); + reason_shift = (long)of_device_get_match_data(&pdev->dev); if (reason_shift != NO_REASON_SHIFT) { @@ -106,3 +120,4 @@ module_platform_driver(qcom_pon_driver); MODULE_DESCRIPTION("Qualcomm Power On driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/spmi/spmi-devres.c b/drivers/spmi/spmi-devres.c index 62c4b3f24d06..c3e889fe1b6e 100644 --- a/drivers/spmi/spmi-devres.c +++ b/drivers/spmi/spmi-devres.c @@ -60,5 +60,29 @@ int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl } EXPORT_SYMBOL_GPL(devm_spmi_controller_add); +static void devm_spmi_subdevice_remove(void *sub_sdev) +{ + spmi_subdevice_remove(sub_sdev); +} + +struct spmi_subdevice *devm_spmi_subdevice_alloc_and_add(struct device *dev, + struct spmi_device *sparent) +{ + struct spmi_subdevice *sub_sdev; + int ret; + + sub_sdev = spmi_subdevice_alloc_and_add(sparent); + if (IS_ERR(sub_sdev)) + return sub_sdev; + + ret = devm_add_action_or_reset(dev, devm_spmi_subdevice_remove, sub_sdev); + if (ret) + return ERR_PTR(ret); + + return sub_sdev; +} +EXPORT_SYMBOL_NS_GPL(devm_spmi_subdevice_alloc_and_add, "SPMI"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SPMI devres helpers"); +MODULE_IMPORT_NS("SPMI"); diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c index 1048420b5afb..833c2b30d09f 100644 --- a/drivers/spmi/spmi-mtk-pmif.c +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -21,6 +21,7 @@ #define SWINF_WFVLDCLR 0x06 #define GET_SWINF(x) (((x) >> 1) & 0x7) +#define GET_SWINFERR(x) (((x) >> 18) & 0x1) #define PMIF_CMD_REG_0 0 #define PMIF_CMD_REG 1 @@ -349,6 +350,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, struct pmif *arb = to_mtk_pmif(ctrl); struct ch_reg *inf_reg; int ret; + u8 retry = 0; u32 data, cmd; unsigned long flags; @@ -385,17 +387,35 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, return ret; } - /* Send the command. */ cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr; - pmif_writel(arb, pbus, cmd, inf_reg->ch_send); + do { + /* Send the command. */ + pmif_writel(arb, pbus, cmd, inf_reg->ch_send); + + /* + * Wait for Software Interface FSM state to be WFVLDCLR or to + * return an error. + * + * If this is WFVLDCLR, read the data and clear the valid flag; + * If error or timeout, retry for a maximum of 3 times as a + * workaround for an hardware issue. + */ + ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta], + data, + GET_SWINF(data) == SWINF_WFVLDCLR || + GET_SWINFERR(data), + PMIF_DELAY_US, PMIF_TIMEOUT_US / 2); + if (ret < 0) + continue; + + if (GET_SWINFERR(data)) { + ret = -EIO; + continue; + } + + break; + } while (++retry < 3); - /* - * Wait for Software Interface FSM state to be WFVLDCLR, - * read the data and clear the valid flag. - */ - ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta], - data, GET_SWINF(data) == SWINF_WFVLDCLR, - PMIF_DELAY_US, PMIF_TIMEOUT_US); if (ret < 0) { raw_spin_unlock_irqrestore(&pbus->lock, flags); dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n"); @@ -726,9 +746,11 @@ static int mtk_spmi_bus_probe(struct platform_device *pdev, for (i = 0; i < pbus->nclks; i++) { pbus->clks[i].id = pmif_clock_names[i]; pbus->clks[i].clk = of_clk_get_by_name(node, pbus->clks[i].id); - if (IS_ERR(pbus->clks[i].clk)) + if (IS_ERR(pbus->clks[i].clk)) { + clk_bulk_put(i, pbus->clks); return dev_err_probe(&pdev->dev, PTR_ERR(pbus->clks[i].clk), "Failed to get clocks\n"); + } } err = clk_bulk_prepare_enable(pbus->nclks, pbus->clks); diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 2e2cb4774103..402ffeaff8e2 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -2060,7 +2060,7 @@ static int spmi_pmic_arb_register_buses(struct spmi_pmic_arb *pmic_arb, } } - return ret; + return 0; } static void spmi_pmic_arb_deregister_buses(struct spmi_pmic_arb *pmic_arb) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 57b7c0cb4240..ec97f12046f5 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -19,11 +19,13 @@ static bool is_registered; static DEFINE_IDA(ctrl_ida); +static DEFINE_IDA(spmi_subdevice_ida); static void spmi_dev_release(struct device *dev) { struct spmi_device *sdev = to_spmi_device(dev); + of_node_put(dev->of_node); kfree(sdev); } @@ -31,6 +33,19 @@ static const struct device_type spmi_dev_type = { .release = spmi_dev_release, }; +static void spmi_subdev_release(struct device *dev) +{ + struct spmi_device *sdev = to_spmi_device(dev); + struct spmi_subdevice *sub_sdev = container_of(sdev, struct spmi_subdevice, sdev); + + ida_free(&spmi_subdevice_ida, sub_sdev->devid); + kfree(sub_sdev); +} + +static const struct device_type spmi_subdev_type = { + .release = spmi_subdev_release, +}; + static void spmi_ctrl_release(struct device *dev) { struct spmi_controller *ctrl = to_spmi_controller(dev); @@ -68,15 +83,12 @@ int spmi_device_add(struct spmi_device *sdev) err = device_add(&sdev->dev); if (err < 0) { - dev_err(&sdev->dev, "Can't add %s, status %d\n", - dev_name(&sdev->dev), err); - goto err_device_add; + dev_err(&sdev->dev, "Can't add device, status %pe\n", ERR_PTR(err)); + return err; } - dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); - -err_device_add: - return err; + dev_dbg(&sdev->dev, "device registered\n"); + return 0; } EXPORT_SYMBOL_GPL(spmi_device_add); @@ -90,6 +102,18 @@ void spmi_device_remove(struct spmi_device *sdev) } EXPORT_SYMBOL_GPL(spmi_device_remove); +/** + * spmi_subdevice_remove() - Remove an SPMI subdevice + * @sub_sdev: spmi_device to be removed + */ +void spmi_subdevice_remove(struct spmi_subdevice *sub_sdev) +{ + struct spmi_device *sdev = &sub_sdev->sdev; + + device_unregister(&sdev->dev); +} +EXPORT_SYMBOL_NS_GPL(spmi_subdevice_remove, "SPMI"); + static inline int spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) { @@ -407,6 +431,29 @@ struct spmi_device *spmi_find_device_by_of_node(struct device_node *np) } EXPORT_SYMBOL_GPL(spmi_find_device_by_of_node); +/** + * spmi_get_parent_spmi_device() - get the parent SPMI device from current dev + * @dev: pointer to a subdevice on SPMI bus + * + * Checks if the passed device is a child of an SPMI device and returns a + * handle to the parent SPMI device without incrementing any refcount. + * + * Return: Handle to the parent SPMI device or NULL + */ +struct spmi_device *spmi_get_parent_spmi_device(struct device *dev) +{ + struct device *parent; + + if (dev && dev->parent) { + parent = dev->parent; + + if (parent->type == &spmi_dev_type) + return to_spmi_device(parent); + } + return NULL; +} +EXPORT_SYMBOL_NS_GPL(spmi_get_parent_spmi_device, "SPMI"); + /** * spmi_device_alloc() - Allocate a new SPMI device * @ctrl: associated controller @@ -431,6 +478,58 @@ struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) } EXPORT_SYMBOL_GPL(spmi_device_alloc); +/** + * spmi_subdevice_alloc_and_add(): Allocate and add a new SPMI sub-device + * @sparent: SPMI parent device with previously registered SPMI controller + * + * Returns: + * Pointer to newly allocated SPMI sub-device for success or error pointer. + */ +struct spmi_subdevice *spmi_subdevice_alloc_and_add(struct spmi_device *sparent) +{ + struct spmi_subdevice *sub_sdev; + struct spmi_device *sdev; + int ret; + + sub_sdev = kzalloc_obj(*sub_sdev); + if (!sub_sdev) + return ERR_PTR(-ENOMEM); + + sdev = &sub_sdev->sdev; + sdev->ctrl = sparent->ctrl; + sdev->usid = sparent->usid; + + ret = ida_alloc(&spmi_subdevice_ida, GFP_KERNEL); + if (ret < 0) { + kfree(sub_sdev); + return ERR_PTR(ret); + } + sub_sdev->devid = ret; + + device_initialize(&sdev->dev); + sdev->dev.parent = &sparent->dev; + sdev->dev.bus = &spmi_bus_type; + sdev->dev.type = &spmi_subdev_type; + + ret = dev_set_name(&sdev->dev, "%u-%02x.%u.auto", + sdev->ctrl->nr, sdev->usid, sub_sdev->devid); + if (ret) + goto err_put_dev; + + ret = device_add(&sdev->dev); + if (ret) { + dev_err(&sdev->dev, "Can't add device, status %pe\n", ERR_PTR(ret)); + goto err_put_dev; + } + + return sub_sdev; + +err_put_dev: + put_device(&sdev->dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_NS_GPL(spmi_subdevice_alloc_and_add, "SPMI"); + /** * spmi_controller_alloc() - Allocate a new SPMI controller * @parent: parent device @@ -494,8 +593,8 @@ static void of_spmi_register_devices(struct spmi_controller *ctrl) err = of_property_read_u32_array(node, "reg", reg, 2); if (err) { dev_err(&ctrl->dev, - "node %pOF err (%d) does not have 'reg' property\n", - node, err); + "node %pOF err (%pe) does not have 'reg' property\n", + node, ERR_PTR(err)); continue; } @@ -517,13 +616,13 @@ static void of_spmi_register_devices(struct spmi_controller *ctrl) if (!sdev) continue; - device_set_node(&sdev->dev, of_fwnode_handle(node)); + device_set_node(&sdev->dev, of_fwnode_handle(of_node_get(node))); sdev->usid = (u8)reg[0]; err = spmi_device_add(sdev); if (err) { dev_err(&sdev->dev, - "failure adding device. status %d\n", err); + "failure adding device. status %pe\n", ERR_PTR(err)); spmi_device_put(sdev); } } diff --git a/include/linux/spmi.h b/include/linux/spmi.h index 4eb9564a7fb3..4daebc980501 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -69,6 +69,22 @@ int spmi_device_add(struct spmi_device *sdev); void spmi_device_remove(struct spmi_device *sdev); +/** + * struct spmi_subdevice - Basic representation of an SPMI sub-device + * @sdev: Sub-device representation of an SPMI device + * @devid: Platform Device ID of an SPMI sub-device + */ +struct spmi_subdevice { + struct spmi_device sdev; + unsigned int devid; +}; + +struct spmi_subdevice *spmi_subdevice_alloc_and_add(struct spmi_device *sparent); +void spmi_subdevice_remove(struct spmi_subdevice *sdev); + +struct spmi_subdevice *devm_spmi_subdevice_alloc_and_add(struct device *dev, + struct spmi_device *sparent); + /** * struct spmi_controller - interface to the SPMI master controller * @dev: Driver model representation of the device. @@ -173,6 +189,7 @@ static inline void spmi_driver_unregister(struct spmi_driver *sdrv) struct device_node; struct spmi_device *spmi_find_device_by_of_node(struct device_node *np); +struct spmi_device *spmi_get_parent_spmi_device(struct device *dev); int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf); int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, size_t len);