162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2010, Google Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Original authors: Code Aurora Forum
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Dima Zavin <dima@android.com>
862306a36Sopenharmony_ci *  - Largely rewritten from original to not be an i2c driver.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_platform.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/ssbi.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* SSBI 2.0 controller registers */
2562306a36Sopenharmony_ci#define SSBI2_CMD			0x0008
2662306a36Sopenharmony_ci#define SSBI2_RD			0x0010
2762306a36Sopenharmony_ci#define SSBI2_STATUS			0x0014
2862306a36Sopenharmony_ci#define SSBI2_MODE2			0x001C
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* SSBI_CMD fields */
3162306a36Sopenharmony_ci#define SSBI_CMD_RDWRN			(1 << 24)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* SSBI_STATUS fields */
3462306a36Sopenharmony_ci#define SSBI_STATUS_RD_READY		(1 << 2)
3562306a36Sopenharmony_ci#define SSBI_STATUS_READY		(1 << 1)
3662306a36Sopenharmony_ci#define SSBI_STATUS_MCHN_BUSY		(1 << 0)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* SSBI_MODE2 fields */
3962306a36Sopenharmony_ci#define SSBI_MODE2_REG_ADDR_15_8_SHFT	0x04
4062306a36Sopenharmony_ci#define SSBI_MODE2_REG_ADDR_15_8_MASK	(0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
4362306a36Sopenharmony_ci	(((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
4462306a36Sopenharmony_ci	SSBI_MODE2_REG_ADDR_15_8_MASK))
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* SSBI PMIC Arbiter command registers */
4762306a36Sopenharmony_ci#define SSBI_PA_CMD			0x0000
4862306a36Sopenharmony_ci#define SSBI_PA_RD_STATUS		0x0004
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* SSBI_PA_CMD fields */
5162306a36Sopenharmony_ci#define SSBI_PA_CMD_RDWRN		(1 << 24)
5262306a36Sopenharmony_ci#define SSBI_PA_CMD_ADDR_MASK		0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* SSBI_PA_RD_STATUS fields */
5562306a36Sopenharmony_ci#define SSBI_PA_RD_STATUS_TRANS_DONE	(1 << 27)
5662306a36Sopenharmony_ci#define SSBI_PA_RD_STATUS_TRANS_DENIED	(1 << 26)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define SSBI_TIMEOUT_US			100
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cienum ssbi_controller_type {
6162306a36Sopenharmony_ci	MSM_SBI_CTRL_SSBI = 0,
6262306a36Sopenharmony_ci	MSM_SBI_CTRL_SSBI2,
6362306a36Sopenharmony_ci	MSM_SBI_CTRL_PMIC_ARBITER,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct ssbi {
6762306a36Sopenharmony_ci	struct device		*slave;
6862306a36Sopenharmony_ci	void __iomem		*base;
6962306a36Sopenharmony_ci	spinlock_t		lock;
7062306a36Sopenharmony_ci	enum ssbi_controller_type controller_type;
7162306a36Sopenharmony_ci	int (*read)(struct ssbi *, u16 addr, u8 *buf, int len);
7262306a36Sopenharmony_ci	int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len);
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return readl(ssbi->base + reg);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	writel(val, ssbi->base + reg);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * Via private exchange with one of the original authors, the hardware
8762306a36Sopenharmony_ci * should generally finish a transaction in about 5us.  The worst
8862306a36Sopenharmony_ci * case, is when using the arbiter and both other CPUs have just
8962306a36Sopenharmony_ci * started trying to use the SSBI bus will result in a time of about
9062306a36Sopenharmony_ci * 20us.  It should never take longer than this.
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * As such, this wait merely spins, with a udelay.
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	u32 timeout = SSBI_TIMEOUT_US;
9762306a36Sopenharmony_ci	u32 val;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	while (timeout--) {
10062306a36Sopenharmony_ci		val = ssbi_readl(ssbi, SSBI2_STATUS);
10162306a36Sopenharmony_ci		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
10262306a36Sopenharmony_ci			return 0;
10362306a36Sopenharmony_ci		udelay(1);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return -ETIMEDOUT;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int
11062306a36Sopenharmony_cissbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
11362306a36Sopenharmony_ci	int ret = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
11662306a36Sopenharmony_ci		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
11762306a36Sopenharmony_ci		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
11862306a36Sopenharmony_ci		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	while (len) {
12262306a36Sopenharmony_ci		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
12362306a36Sopenharmony_ci		if (ret)
12462306a36Sopenharmony_ci			goto err;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		ssbi_writel(ssbi, cmd, SSBI2_CMD);
12762306a36Sopenharmony_ci		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
12862306a36Sopenharmony_ci		if (ret)
12962306a36Sopenharmony_ci			goto err;
13062306a36Sopenharmony_ci		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
13162306a36Sopenharmony_ci		len--;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cierr:
13562306a36Sopenharmony_ci	return ret;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int
13962306a36Sopenharmony_cissbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int ret = 0;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
14462306a36Sopenharmony_ci		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
14562306a36Sopenharmony_ci		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
14662306a36Sopenharmony_ci		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	while (len) {
15062306a36Sopenharmony_ci		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
15162306a36Sopenharmony_ci		if (ret)
15262306a36Sopenharmony_ci			goto err;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
15562306a36Sopenharmony_ci		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
15662306a36Sopenharmony_ci		if (ret)
15762306a36Sopenharmony_ci			goto err;
15862306a36Sopenharmony_ci		buf++;
15962306a36Sopenharmony_ci		len--;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cierr:
16362306a36Sopenharmony_ci	return ret;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * See ssbi_wait_mask for an explanation of the time and the
16862306a36Sopenharmony_ci * busywait.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic inline int
17162306a36Sopenharmony_cissbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	u32 timeout = SSBI_TIMEOUT_US;
17462306a36Sopenharmony_ci	u32 rd_status = 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	while (timeout--) {
17962306a36Sopenharmony_ci		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED)
18262306a36Sopenharmony_ci			return -EPERM;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
18562306a36Sopenharmony_ci			if (data)
18662306a36Sopenharmony_ci				*data = rd_status & 0xff;
18762306a36Sopenharmony_ci			return 0;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci		udelay(1);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return -ETIMEDOUT;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int
19662306a36Sopenharmony_cissbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	u32 cmd;
19962306a36Sopenharmony_ci	int ret = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	while (len) {
20462306a36Sopenharmony_ci		ret = ssbi_pa_transfer(ssbi, cmd, buf);
20562306a36Sopenharmony_ci		if (ret)
20662306a36Sopenharmony_ci			goto err;
20762306a36Sopenharmony_ci		buf++;
20862306a36Sopenharmony_ci		len--;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cierr:
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int
21662306a36Sopenharmony_cissbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	u32 cmd;
21962306a36Sopenharmony_ci	int ret = 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	while (len) {
22262306a36Sopenharmony_ci		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
22362306a36Sopenharmony_ci		ret = ssbi_pa_transfer(ssbi, cmd, NULL);
22462306a36Sopenharmony_ci		if (ret)
22562306a36Sopenharmony_ci			goto err;
22662306a36Sopenharmony_ci		buf++;
22762306a36Sopenharmony_ci		len--;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cierr:
23162306a36Sopenharmony_ci	return ret;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ciint ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct ssbi *ssbi = dev_get_drvdata(dev);
23762306a36Sopenharmony_ci	unsigned long flags;
23862306a36Sopenharmony_ci	int ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	spin_lock_irqsave(&ssbi->lock, flags);
24162306a36Sopenharmony_ci	ret = ssbi->read(ssbi, addr, buf, len);
24262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ssbi->lock, flags);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return ret;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ssbi_read);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciint ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct ssbi *ssbi = dev_get_drvdata(dev);
25162306a36Sopenharmony_ci	unsigned long flags;
25262306a36Sopenharmony_ci	int ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	spin_lock_irqsave(&ssbi->lock, flags);
25562306a36Sopenharmony_ci	ret = ssbi->write(ssbi, addr, buf, len);
25662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ssbi->lock, flags);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return ret;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ssbi_write);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int ssbi_probe(struct platform_device *pdev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
26562306a36Sopenharmony_ci	struct ssbi *ssbi;
26662306a36Sopenharmony_ci	const char *type;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ssbi = devm_kzalloc(&pdev->dev, sizeof(*ssbi), GFP_KERNEL);
26962306a36Sopenharmony_ci	if (!ssbi)
27062306a36Sopenharmony_ci		return -ENOMEM;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ssbi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
27362306a36Sopenharmony_ci	if (IS_ERR(ssbi->base))
27462306a36Sopenharmony_ci		return PTR_ERR(ssbi->base);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	platform_set_drvdata(pdev, ssbi);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	type = of_get_property(np, "qcom,controller-type", NULL);
27962306a36Sopenharmony_ci	if (type == NULL) {
28062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Missing qcom,controller-type property\n");
28162306a36Sopenharmony_ci		return -EINVAL;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
28462306a36Sopenharmony_ci	if (strcmp(type, "ssbi") == 0)
28562306a36Sopenharmony_ci		ssbi->controller_type = MSM_SBI_CTRL_SSBI;
28662306a36Sopenharmony_ci	else if (strcmp(type, "ssbi2") == 0)
28762306a36Sopenharmony_ci		ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
28862306a36Sopenharmony_ci	else if (strcmp(type, "pmic-arbiter") == 0)
28962306a36Sopenharmony_ci		ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
29062306a36Sopenharmony_ci	else {
29162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unknown qcom,controller-type\n");
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
29662306a36Sopenharmony_ci		ssbi->read = ssbi_pa_read_bytes;
29762306a36Sopenharmony_ci		ssbi->write = ssbi_pa_write_bytes;
29862306a36Sopenharmony_ci	} else {
29962306a36Sopenharmony_ci		ssbi->read = ssbi_read_bytes;
30062306a36Sopenharmony_ci		ssbi->write = ssbi_write_bytes;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	spin_lock_init(&ssbi->lock);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return devm_of_platform_populate(&pdev->dev);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const struct of_device_id ssbi_match_table[] = {
30962306a36Sopenharmony_ci	{ .compatible = "qcom,ssbi" },
31062306a36Sopenharmony_ci	{}
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ssbi_match_table);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic struct platform_driver ssbi_driver = {
31562306a36Sopenharmony_ci	.probe		= ssbi_probe,
31662306a36Sopenharmony_ci	.driver		= {
31762306a36Sopenharmony_ci		.name	= "ssbi",
31862306a36Sopenharmony_ci		.of_match_table = ssbi_match_table,
31962306a36Sopenharmony_ci	},
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_cimodule_platform_driver(ssbi_driver);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
32462306a36Sopenharmony_ciMODULE_VERSION("1.0");
32562306a36Sopenharmony_ciMODULE_ALIAS("platform:ssbi");
32662306a36Sopenharmony_ciMODULE_AUTHOR("Dima Zavin <dima@android.com>");
327