mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
More than a handful of changes for SPMI. I'm bringing on Abel
Vesa to help maintain SPMI so the tree may change in the future but we'll probably continue to send patches when the pile is small. The big change here is that we gained a new function spmi_subdevice_alloc_and_add() to support complex PMICs that have different devices spread throughout the SPMI address. We've grown subdevice drivers that use OF APIs directly to figure out some base address to add to all their register read/write operations which is tightly coupling our parent/child drivers. This change allows us to clean all those drivers up to use standard device APIs and their own regmap instead of relying on a parent SPMI driver to do all the heavy lifting. The series also updated drivers that could use the new API and I picked those all up because they were acked by maintainers. Beyond that we have some minor fixes to the Qualcomm and Mediatek SPMI drivers for bugs in software and hardware and a couple maintainer updates as stated earlier. -----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEE9L57QeeUxqYDyoaDrQKIl8bklSUFAmrUXP4UHHN3Ym95ZEBj aHJvbWl1bS5vcmcACgkQrQKIl8bklSVZ4A/+P0bQBgIiAjklv1cKSm89t8G4uGM1 it0eqGzmL4BLVtXnwdv+dRsIRe+T3oBRxf45aEvbCvGby0s+UsRIvg3dX+cnDogQ jvr6a1/wfkjoe44cdhgizfZJaURAUsSTdWhfXlAkAovtEAyBHVUOmIDBz/9BD0ni KrCVWaKEdbKu5IbPcEkIrGVJ+RCLTy0Ke0MbQS2+bmRVul6frfLf5RHMqu9dlgWz irU4560aL1tsPKjXIHVC3vIdhowltInfOmtf0DLF3ExF6bJc5D8cXcCz3036WEvu 1bzrjxLbYFatCtKXdUM9SrUGp1gXaQruccaNEr3ze9PV7jt3XV6ehsWaolOcWrGz /B1e0wG0XAMETZ34IrAI7l/DAPJdTY/brMATOQq3qXYMzwmyziD32ZdlxTV5b6wJ tRfX3hxls/42Ii4UEPiHXzaacO4tpywJOmMa0MY9QumjfbqksIuzGGZsTfYrgLdg X1RRcm+VqrMCE+ylFNrwBPybVkjLb5p5HEGdC0IedKw8PcqzVQ4M3ea5X2gdaFh4 i8xZHfF3XifDNBbQLg4E3qNriAQhrz7Ax8qSAP/JLGRgNRijAFWVOPeGPsWLRIhY 3Sq6KX4T5RKHg3N2ertmIyPM/eTj43iJT+kQSdhaerJ+Q79olBlhh3LdgeW9zhoq kXK4GqNlFeIx+aI= =1IIa -----END PGP SIGNATURE----- Merge tag 'spmi-for-next' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi into char-misc-next Stephen writes: More than a handful of changes for SPMI. I'm bringing on Abel Vesa to help maintain SPMI so the tree may change in the future but we'll probably continue to send patches when the pile is small. The big change here is that we gained a new function spmi_subdevice_alloc_and_add() to support complex PMICs that have different devices spread throughout the SPMI address. We've grown subdevice drivers that use OF APIs directly to figure out some base address to add to all their register read/write operations which is tightly coupling our parent/child drivers. This change allows us to clean all those drivers up to use standard device APIs and their own regmap instead of relying on a parent SPMI driver to do all the heavy lifting. The series also updated drivers that could use the new API and I picked those all up because they were acked by maintainers. Beyond that we have some minor fixes to the Qualcomm and Mediatek SPMI drivers for bugs in software and hardware and a couple maintainer updates as stated earlier. * tag 'spmi-for-next' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi: MAINTAINERS: Add Abel as SPMI co-maintainer spmi: mtk-pmif: Fix clock reference leak in mtk_spmi_bus_probe() iio: adc: qcom-spmi-iadc: Remove regmap R/W wrapper functions iio: adc: qcom-spmi-iadc: Migrate to devm_spmi_subdevice_alloc_and_add() misc: qcom-coincell: Migrate to devm_spmi_subdevice_alloc_and_add() phy: qualcomm: eusb2-repeater: Migrate to devm_spmi_subdevice_alloc_and_add() power: reset: qcom-pon: Migrate to devm_spmi_subdevice_alloc_and_add() nvmem: qcom-spmi-sdam: Migrate to devm_spmi_subdevice_alloc_and_add() spmi: Add helper to get a parent SPMI device spmi: Implement spmi_subdevice_alloc_and_add() and devm variant spmi: Remove unneeded goto in spmi_device_add() error path spmi: Print error status with %pe format spmi: Remove redundant dev_name() print in spmi_device_add() spmi: Fix potential use-after-free by grabbing of_node reference spmi: mtk-pmif: Add workaround for FSM lockup/error in read operation MAINTAINERS: Add Qualcomm SPMI controller entry spmi: pmic-arb: return 0 in spmi_pmic_arb_register_buses() on success
This commit is contained in:
commit
10c833da4d
|
|
@ -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 <abelvesa@kernel.org>
|
||||
M: David Collins <david.collins@oss.qualcomm.com>
|
||||
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 <amirreza.zarrabi@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
|
|
@ -25918,6 +25926,7 @@ F: tools/spi/
|
|||
|
||||
SPMI SUBSYSTEM
|
||||
M: Stephen Boyd <sboyd@kernel.org>
|
||||
M: Abel Vesa <abelvesa@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
/* 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 <iivanov@mm-sol.com>");
|
||||
MODULE_IMPORT_NS("SPMI");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
#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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
/* 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");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/reboot.h>
|
||||
#include <linux/reboot-mode.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
#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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user