Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78ab4086 authored by Devesh Jhunjhunwala's avatar Devesh Jhunjhunwala
Browse files

msm: qpnp-misc: add support to configure PWM source



Add the qcom,pwm-sel and qcom,enable-gp-driver properties to
the MISC device driver to enable configuring the PWM source.
Currently this will be supported only for pmdcalifornium.

CRs-Fixed: 972331
Change-Id: I320edac22b20d748a531a41eaff06162092cfd3d
Signed-off-by: default avatarDevesh Jhunjhunwala <deveshj@codeaurora.org>
parent 8d800501
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -6,6 +6,16 @@ Required properties:
- compatible : should be "qcom,qpnp-misc"
- reg : offset and length of the PMIC peripheral register map.

Optional properties:
- qcom,pwm-sel:			Select PWM source. Possible values:
				0: LOW
				1: PWM1_in
				2: PWM2_in
				3: PWM1_in & PWM2_in
- qcom,enable-gp-driver:	Enable the GP driver. Should only be specified
				if a non-zero PWM source is specified under
				"qcom,pwm-sel" property.

Example:
	qcom,spmi@fc4c0000 {
		#address-cells = <1>;
@@ -22,6 +32,8 @@ Example:
			qcom,misc@900 {
				compatible = "qcom,qpnp-misc";
				reg = <0x900 0x100>;
				qcom,pwm-sel = <2>;
				qcom,enable-gp-driver;
			};
		}
	};
+147 −28
Original line number Diff line number Diff line
@@ -23,10 +23,20 @@

#define REG_DIG_MAJOR_REV	0x01
#define REG_SUBTYPE		0x05
#define REG_PWM_SEL		0x49
#define REG_GP_DRIVER_EN	0x4C

#define PWM_SEL_MAX		0x03
#define GP_DRIVER_EN_BIT	BIT(0)

static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
static LIST_HEAD(qpnp_misc_dev_list);

struct qpnp_misc_version {
	u8	subtype;
	u8	dig_major_rev;
};

/**
 * struct qpnp_misc_dev - holds controller device specific information
 * @list:			Doubly-linked list parameter linking to other
@@ -37,6 +47,8 @@ static LIST_HEAD(qpnp_misc_dev_list);
 * @dev:			Device pointer to the misc device
 * @resource:			Resource pointer that holds base address
 * @spmi:			Spmi pointer which holds spmi information
 * @version:			struct that holds the subtype and dig_major_rev
 *				of the chip.
 */
struct qpnp_misc_dev {
	struct list_head		list;
@@ -44,11 +56,10 @@ struct qpnp_misc_dev {
	struct device			*dev;
	struct resource			*resource;
	struct spmi_device		*spmi;
};
	struct qpnp_misc_version	version;

struct qpnp_misc_version {
	u8				subtype;
	u8				dig_major_rev;
	u8				pwm_sel;
	bool				enable_gp_driver;
};

static struct of_device_id qpnp_misc_match_table[] = {
@@ -56,43 +67,65 @@ static struct of_device_id qpnp_misc_match_table[] = {
	{}
};

static u8 qpnp_read_byte(struct spmi_device *spmi, u16 addr)
enum qpnp_misc_version_name {
	INVALID,
	PM8941,
	PM8226,
	PMA8084,
	PMDCALIFORNIUM,
};

static struct qpnp_misc_version irq_support_version[] = {
	{0x00, 0x00}, /* INVALID */
	{0x01, 0x02}, /* PM8941 */
	{0x07, 0x00}, /* PM8226 */
	{0x09, 0x00}, /* PMA8084 */
	{0x16, 0x00}, /* PMDCALIFORNIUM */
};

static int qpnp_write_byte(struct spmi_device *spmi, u16 addr, u8 val)
{
	int rc;
	u8 val;

	rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, &val, 1);
	rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, &val, 1);
	if (rc)
		pr_err("SPMI write failed rc=%d\n", rc);

	return rc;
}

static int qpnp_read_byte(struct spmi_device *spmi, u16 addr, u8 *val)
{
	int rc;

	rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, 1);
	if (rc) {
		pr_err("SPMI read failed rc=%d\n", rc);
		return 0;
		return rc;
	}
	return val;
	return rc;
}

static struct qpnp_misc_version irq_support_version[] = {
	{0x01, 0x02}, /* PM8941 */
	{0x07, 0x00}, /* PM8226 */
	{0x09, 0x00}, /* PMA8084 */
};

static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
static int get_qpnp_misc_version_name(struct qpnp_misc_dev *dev)
{
	int i;
	u8 subtype, dig_major_rev;

	subtype = qpnp_read_byte(dev->spmi, dev->resource->start + REG_SUBTYPE);
	pr_debug("subtype = 0x%02X\n", subtype);
	for (i = 1; i < ARRAY_SIZE(irq_support_version); i++)
		if (dev->version.subtype == irq_support_version[i].subtype &&
		    dev->version.dig_major_rev >=
					irq_support_version[i].dig_major_rev)
			return i;

	dig_major_rev = qpnp_read_byte(dev->spmi,
		dev->resource->start + REG_DIG_MAJOR_REV);
	pr_debug("dig_major rev = 0x%02X\n", dig_major_rev);
	return INVALID;
}

	for (i = 0; i < ARRAY_SIZE(irq_support_version); i++)
		if (subtype == irq_support_version[i].subtype
		    && dig_major_rev >= irq_support_version[i].dig_major_rev)
			return 1;
static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
{
	int version_name = get_qpnp_misc_version_name(dev);

	if (version_name == INVALID)
		return 0;
	return 1;
}

int qpnp_misc_irqs_available(struct device *consumer_dev)
@@ -133,10 +166,68 @@ int qpnp_misc_irqs_available(struct device *consumer_dev)
	return __misc_irqs_available(mdev_found);
}

static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
{
	u32 val;

	if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) {
		if (val > PWM_SEL_MAX) {
			dev_err(mdev->dev, "Invalid value for pwm-sel\n");
			return -EINVAL;
		}
		mdev->pwm_sel = (u8)val;
	}
	mdev->enable_gp_driver = of_property_read_bool(mdev->dev->of_node,
						"qcom,enable-gp-driver");

	WARN((mdev->pwm_sel > 0 && !mdev->enable_gp_driver),
			"Setting PWM source without enabling gp driver\n");
	WARN((mdev->pwm_sel == 0 && mdev->enable_gp_driver),
			"Enabling gp driver without setting PWM source\n");

	return 0;
}

static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
{
	int rc, version_name;

	version_name = get_qpnp_misc_version_name(mdev);

	switch (version_name) {
	case PMDCALIFORNIUM:
		if (mdev->pwm_sel > 0 && mdev->enable_gp_driver) {
			rc = qpnp_write_byte(mdev->spmi,
				mdev->resource->start + REG_PWM_SEL,
				mdev->pwm_sel);
			if (rc < 0) {
				dev_err(mdev->dev,
					"Failed to write PWM_SEL reg\n");
				return rc;
			}

			rc = qpnp_write_byte(mdev->spmi,
				mdev->resource->start + REG_GP_DRIVER_EN,
				GP_DRIVER_EN_BIT);
			if (rc < 0) {
				dev_err(mdev->dev,
					"Failed to write GP_DRIVER_EN reg\n");
				return rc;
			}
		}
		break;
	default:
		break;
	}

	return 0;
}

static int qpnp_misc_probe(struct spmi_device *spmi)
{
	struct resource *resource;
	struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL);
	int rc;

	resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
	if (!resource) {
@@ -152,11 +243,39 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
	mdev->dev = &(spmi->dev);
	mdev->resource = resource;

	rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE,
				&mdev->version.subtype);
	if (rc < 0) {
		dev_err(mdev->dev, "Failed to read subtype, rc=%d\n", rc);
		return rc;
	}

	rc = qpnp_read_byte(spmi, resource->start + REG_DIG_MAJOR_REV,
				&mdev->version.dig_major_rev);
	if (rc < 0) {
		dev_err(mdev->dev, "Failed to read dig_major_rev, rc=%d\n", rc);
		return rc;
	}

	mutex_lock(&qpnp_misc_dev_list_mutex);
	list_add_tail(&mdev->list, &qpnp_misc_dev_list);
	mutex_unlock(&qpnp_misc_dev_list_mutex);

	pr_debug("probed successfully\n");
	rc = qpnp_misc_dt_init(mdev);
	if (rc < 0) {
		dev_err(mdev->dev,
			"Error reading device tree properties, rc=%d\n", rc);
		return rc;
	}

	rc = qpnp_misc_config(mdev);
	if (rc < 0) {
		dev_err(mdev->dev,
			"Error configuring module registers, rc=%d\n", rc);
		return rc;
	}

	dev_info(mdev->dev, "probe successful\n");
	return 0;
}