xref: /rk3399_rockchip-uboot/drivers/spmi/spmi-msm.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
15b47271cSMateusz Kulikowski /*
25b47271cSMateusz Kulikowski  * Qualcomm SPMI bus driver
35b47271cSMateusz Kulikowski  *
45b47271cSMateusz Kulikowski  * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
55b47271cSMateusz Kulikowski  *
65b47271cSMateusz Kulikowski  * Loosely based on Little Kernel driver
75b47271cSMateusz Kulikowski  *
85b47271cSMateusz Kulikowski  * SPDX-License-Identifier:	BSD-3-Clause
95b47271cSMateusz Kulikowski  */
105b47271cSMateusz Kulikowski 
115b47271cSMateusz Kulikowski #include <common.h>
125b47271cSMateusz Kulikowski #include <dm.h>
135b47271cSMateusz Kulikowski #include <errno.h>
145b47271cSMateusz Kulikowski #include <fdtdec.h>
155b47271cSMateusz Kulikowski #include <asm/io.h>
165b47271cSMateusz Kulikowski #include <spmi/spmi.h>
175b47271cSMateusz Kulikowski 
185b47271cSMateusz Kulikowski DECLARE_GLOBAL_DATA_PTR;
195b47271cSMateusz Kulikowski 
205b47271cSMateusz Kulikowski #define ARB_CHANNEL_OFFSET(n)		(0x4 * (n))
215b47271cSMateusz Kulikowski #define SPMI_CH_OFFSET(chnl)		((chnl) * 0x8000)
225b47271cSMateusz Kulikowski 
235b47271cSMateusz Kulikowski #define SPMI_REG_CMD0			0x0
245b47271cSMateusz Kulikowski #define SPMI_REG_CONFIG			0x4
255b47271cSMateusz Kulikowski #define SPMI_REG_STATUS			0x8
265b47271cSMateusz Kulikowski #define SPMI_REG_WDATA			0x10
275b47271cSMateusz Kulikowski #define SPMI_REG_RDATA			0x18
285b47271cSMateusz Kulikowski 
295b47271cSMateusz Kulikowski #define SPMI_CMD_OPCODE_SHIFT		27
305b47271cSMateusz Kulikowski #define SPMI_CMD_SLAVE_ID_SHIFT		20
315b47271cSMateusz Kulikowski #define SPMI_CMD_ADDR_SHIFT		12
325b47271cSMateusz Kulikowski #define SPMI_CMD_ADDR_OFFSET_SHIFT	4
335b47271cSMateusz Kulikowski #define SPMI_CMD_BYTE_CNT_SHIFT		0
345b47271cSMateusz Kulikowski 
355b47271cSMateusz Kulikowski #define SPMI_CMD_EXT_REG_WRITE_LONG	0x00
365b47271cSMateusz Kulikowski #define SPMI_CMD_EXT_REG_READ_LONG	0x01
375b47271cSMateusz Kulikowski 
385b47271cSMateusz Kulikowski #define SPMI_STATUS_DONE		0x1
395b47271cSMateusz Kulikowski 
405b47271cSMateusz Kulikowski #define SPMI_MAX_CHANNELS	128
415b47271cSMateusz Kulikowski #define SPMI_MAX_SLAVES		16
425b47271cSMateusz Kulikowski #define SPMI_MAX_PERIPH		256
435b47271cSMateusz Kulikowski 
445b47271cSMateusz Kulikowski struct msm_spmi_priv {
455b47271cSMateusz Kulikowski 	phys_addr_t arb_chnl; /* ARB channel mapping base */
465b47271cSMateusz Kulikowski 	phys_addr_t spmi_core; /* SPMI core */
475b47271cSMateusz Kulikowski 	phys_addr_t spmi_obs; /* SPMI observer */
485b47271cSMateusz Kulikowski 	/* SPMI channel map */
495b47271cSMateusz Kulikowski 	uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
505b47271cSMateusz Kulikowski };
515b47271cSMateusz Kulikowski 
msm_spmi_write(struct udevice * dev,int usid,int pid,int off,uint8_t val)525b47271cSMateusz Kulikowski static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
535b47271cSMateusz Kulikowski 			  uint8_t val)
545b47271cSMateusz Kulikowski {
555b47271cSMateusz Kulikowski 	struct msm_spmi_priv *priv = dev_get_priv(dev);
565b47271cSMateusz Kulikowski 	unsigned channel;
575b47271cSMateusz Kulikowski 	uint32_t reg = 0;
585b47271cSMateusz Kulikowski 
595b47271cSMateusz Kulikowski 	if (usid >= SPMI_MAX_SLAVES)
605b47271cSMateusz Kulikowski 		return -EIO;
615b47271cSMateusz Kulikowski 	if (pid >= SPMI_MAX_PERIPH)
625b47271cSMateusz Kulikowski 		return -EIO;
635b47271cSMateusz Kulikowski 
645b47271cSMateusz Kulikowski 	channel = priv->channel_map[usid][pid];
655b47271cSMateusz Kulikowski 
665b47271cSMateusz Kulikowski 	/* Disable IRQ mode for the current channel*/
675b47271cSMateusz Kulikowski 	writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) +
685b47271cSMateusz Kulikowski 	       SPMI_REG_CONFIG);
695b47271cSMateusz Kulikowski 
705b47271cSMateusz Kulikowski 	/* Write single byte */
715b47271cSMateusz Kulikowski 	writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
725b47271cSMateusz Kulikowski 
735b47271cSMateusz Kulikowski 	/* Prepare write command */
745b47271cSMateusz Kulikowski 	reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
755b47271cSMateusz Kulikowski 	reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
765b47271cSMateusz Kulikowski 	reg |= (pid << SPMI_CMD_ADDR_SHIFT);
775b47271cSMateusz Kulikowski 	reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
785b47271cSMateusz Kulikowski 	reg |= 1; /* byte count */
795b47271cSMateusz Kulikowski 
805b47271cSMateusz Kulikowski 	/* Send write command */
815b47271cSMateusz Kulikowski 	writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
825b47271cSMateusz Kulikowski 
835b47271cSMateusz Kulikowski 	/* Wait till CMD DONE status */
845b47271cSMateusz Kulikowski 	reg = 0;
855b47271cSMateusz Kulikowski 	while (!reg) {
865b47271cSMateusz Kulikowski 		reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) +
875b47271cSMateusz Kulikowski 			    SPMI_REG_STATUS);
885b47271cSMateusz Kulikowski 	}
895b47271cSMateusz Kulikowski 
905b47271cSMateusz Kulikowski 	if (reg ^ SPMI_STATUS_DONE) {
915b47271cSMateusz Kulikowski 		printf("SPMI write failure.\n");
925b47271cSMateusz Kulikowski 		return -EIO;
935b47271cSMateusz Kulikowski 	}
945b47271cSMateusz Kulikowski 
955b47271cSMateusz Kulikowski 	return 0;
965b47271cSMateusz Kulikowski }
975b47271cSMateusz Kulikowski 
msm_spmi_read(struct udevice * dev,int usid,int pid,int off)985b47271cSMateusz Kulikowski static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
995b47271cSMateusz Kulikowski {
1005b47271cSMateusz Kulikowski 	struct msm_spmi_priv *priv = dev_get_priv(dev);
1015b47271cSMateusz Kulikowski 	unsigned channel;
1025b47271cSMateusz Kulikowski 	uint32_t reg = 0;
1035b47271cSMateusz Kulikowski 
1045b47271cSMateusz Kulikowski 	if (usid >= SPMI_MAX_SLAVES)
1055b47271cSMateusz Kulikowski 		return -EIO;
1065b47271cSMateusz Kulikowski 	if (pid >= SPMI_MAX_PERIPH)
1075b47271cSMateusz Kulikowski 		return -EIO;
1085b47271cSMateusz Kulikowski 
1095b47271cSMateusz Kulikowski 	channel = priv->channel_map[usid][pid];
1105b47271cSMateusz Kulikowski 
1115b47271cSMateusz Kulikowski 	/* Disable IRQ mode for the current channel*/
1125b47271cSMateusz Kulikowski 	writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
1135b47271cSMateusz Kulikowski 
1145b47271cSMateusz Kulikowski 	/* Prepare read command */
1155b47271cSMateusz Kulikowski 	reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
1165b47271cSMateusz Kulikowski 	reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
1175b47271cSMateusz Kulikowski 	reg |= (pid << SPMI_CMD_ADDR_SHIFT);
1185b47271cSMateusz Kulikowski 	reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
1195b47271cSMateusz Kulikowski 	reg |= 1; /* byte count */
1205b47271cSMateusz Kulikowski 
1215b47271cSMateusz Kulikowski 	/* Request read */
1225b47271cSMateusz Kulikowski 	writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
1235b47271cSMateusz Kulikowski 
1245b47271cSMateusz Kulikowski 	/* Wait till CMD DONE status */
1255b47271cSMateusz Kulikowski 	reg = 0;
1265b47271cSMateusz Kulikowski 	while (!reg) {
1275b47271cSMateusz Kulikowski 		reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
1285b47271cSMateusz Kulikowski 			    SPMI_REG_STATUS);
1295b47271cSMateusz Kulikowski 	}
1305b47271cSMateusz Kulikowski 
1315b47271cSMateusz Kulikowski 	if (reg ^ SPMI_STATUS_DONE) {
1325b47271cSMateusz Kulikowski 		printf("SPMI read failure.\n");
1335b47271cSMateusz Kulikowski 		return -EIO;
1345b47271cSMateusz Kulikowski 	}
1355b47271cSMateusz Kulikowski 
1365b47271cSMateusz Kulikowski 	/* Read the data */
1375b47271cSMateusz Kulikowski 	return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
1385b47271cSMateusz Kulikowski 		     SPMI_REG_RDATA) & 0xFF;
1395b47271cSMateusz Kulikowski }
1405b47271cSMateusz Kulikowski 
1415b47271cSMateusz Kulikowski static struct dm_spmi_ops msm_spmi_ops = {
1425b47271cSMateusz Kulikowski 	.read = msm_spmi_read,
1435b47271cSMateusz Kulikowski 	.write = msm_spmi_write,
1445b47271cSMateusz Kulikowski };
1455b47271cSMateusz Kulikowski 
msm_spmi_probe(struct udevice * dev)1465b47271cSMateusz Kulikowski static int msm_spmi_probe(struct udevice *dev)
1475b47271cSMateusz Kulikowski {
1485b47271cSMateusz Kulikowski 	struct udevice *parent = dev->parent;
1495b47271cSMateusz Kulikowski 	struct msm_spmi_priv *priv = dev_get_priv(dev);
150e160f7d4SSimon Glass 	int node = dev_of_offset(dev);
1515b47271cSMateusz Kulikowski 	int i;
1525b47271cSMateusz Kulikowski 
153*a821c4afSSimon Glass 	priv->arb_chnl = devfdt_get_addr(dev);
1545b47271cSMateusz Kulikowski 	priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
155e160f7d4SSimon Glass 			dev_of_offset(parent), node, "reg", 1, NULL, false);
1565b47271cSMateusz Kulikowski 	priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
157e160f7d4SSimon Glass 			dev_of_offset(parent), node, "reg", 2, NULL, false);
1585b47271cSMateusz Kulikowski 	if (priv->arb_chnl == FDT_ADDR_T_NONE ||
1595b47271cSMateusz Kulikowski 	    priv->spmi_core == FDT_ADDR_T_NONE ||
1605b47271cSMateusz Kulikowski 	    priv->spmi_obs == FDT_ADDR_T_NONE)
1615b47271cSMateusz Kulikowski 		return -EINVAL;
1625b47271cSMateusz Kulikowski 
1635b47271cSMateusz Kulikowski 	/* Scan peripherals connected to each SPMI channel */
1645b47271cSMateusz Kulikowski 	for (i = 0; i < SPMI_MAX_CHANNELS ; i++) {
1655b47271cSMateusz Kulikowski 		uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
1665b47271cSMateusz Kulikowski 		uint8_t slave_id = (periph & 0xf0000) >> 16;
1675b47271cSMateusz Kulikowski 		uint8_t pid = (periph & 0xff00) >> 8;
1685b47271cSMateusz Kulikowski 
1695b47271cSMateusz Kulikowski 		priv->channel_map[slave_id][pid] = i;
1705b47271cSMateusz Kulikowski 	}
1715b47271cSMateusz Kulikowski 	return 0;
1725b47271cSMateusz Kulikowski }
1735b47271cSMateusz Kulikowski 
1745b47271cSMateusz Kulikowski static const struct udevice_id msm_spmi_ids[] = {
1755b47271cSMateusz Kulikowski 	{ .compatible = "qcom,spmi-pmic-arb" },
1765b47271cSMateusz Kulikowski 	{ }
1775b47271cSMateusz Kulikowski };
1785b47271cSMateusz Kulikowski 
1795b47271cSMateusz Kulikowski U_BOOT_DRIVER(msm_spmi) = {
1805b47271cSMateusz Kulikowski 	.name = "msm_spmi",
1815b47271cSMateusz Kulikowski 	.id = UCLASS_SPMI,
1825b47271cSMateusz Kulikowski 	.of_match = msm_spmi_ids,
1835b47271cSMateusz Kulikowski 	.ops = &msm_spmi_ops,
1845b47271cSMateusz Kulikowski 	.probe = msm_spmi_probe,
1855b47271cSMateusz Kulikowski 	.priv_auto_alloc_size = sizeof(struct msm_spmi_priv),
1865b47271cSMateusz Kulikowski };
187