brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 3cafdf2 Raw
351 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/delay.h>4#include <linux/err.h>5#include <linux/interrupt.h>6#include <linux/io.h>7#include <linux/kernel.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/seq_file.h>12#include <linux/slab.h>13#include <linux/spmi.h>14 15/*16 * SPMI register addr17 */18#define SPMI_CHANNEL_OFFSET				0x030019#define SPMI_SLAVE_OFFSET				0x2020 21#define SPMI_APB_SPMI_CMD_BASE_ADDR			0x010022 23#define SPMI_APB_SPMI_WDATA0_BASE_ADDR			0x010424#define SPMI_APB_SPMI_WDATA1_BASE_ADDR			0x010825#define SPMI_APB_SPMI_WDATA2_BASE_ADDR			0x010c26#define SPMI_APB_SPMI_WDATA3_BASE_ADDR			0x011027 28#define SPMI_APB_SPMI_STATUS_BASE_ADDR			0x020029 30#define SPMI_APB_SPMI_RDATA0_BASE_ADDR			0x020431#define SPMI_APB_SPMI_RDATA1_BASE_ADDR			0x020832#define SPMI_APB_SPMI_RDATA2_BASE_ADDR			0x020c33#define SPMI_APB_SPMI_RDATA3_BASE_ADDR			0x021034 35#define SPMI_PER_DATAREG_BYTE				436/*37 * SPMI cmd register38 */39#define SPMI_APB_SPMI_CMD_EN				BIT(31)40#define SPMI_APB_SPMI_CMD_TYPE_OFFSET			2441#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET			2042#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET		1643#define SPMI_APB_SPMI_CMD_ADDR_OFFSET			044 45/* Command Opcodes */46 47enum spmi_controller_cmd_op_code {48	SPMI_CMD_REG_ZERO_WRITE = 0,49	SPMI_CMD_REG_WRITE = 1,50	SPMI_CMD_REG_READ = 2,51	SPMI_CMD_EXT_REG_WRITE = 3,52	SPMI_CMD_EXT_REG_READ = 4,53	SPMI_CMD_EXT_REG_WRITE_L = 5,54	SPMI_CMD_EXT_REG_READ_L = 6,55	SPMI_CMD_REG_RESET = 7,56	SPMI_CMD_REG_SLEEP = 8,57	SPMI_CMD_REG_SHUTDOWN = 9,58	SPMI_CMD_REG_WAKEUP = 10,59};60 61/*62 * SPMI status register63 */64#define SPMI_APB_TRANS_DONE			BIT(0)65#define SPMI_APB_TRANS_FAIL			BIT(2)66 67/* Command register fields */68#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT	1669 70/* Maximum number of support PMIC peripherals */71#define SPMI_CONTROLLER_TIMEOUT_US		100072#define SPMI_CONTROLLER_MAX_TRANS_BYTES		1673 74struct spmi_controller_dev {75	struct spmi_controller	*controller;76	struct device		*dev;77	void __iomem		*base;78	spinlock_t		lock;79	u32			channel;80};81 82static int spmi_controller_wait_for_done(struct device *dev,83					 struct spmi_controller_dev *ctrl_dev,84					 void __iomem *base, u8 sid, u16 addr)85{86	u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;87	u32 status, offset;88 89	offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;90	offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;91 92	do {93		status = readl(base + offset);94 95		if (status & SPMI_APB_TRANS_DONE) {96			if (status & SPMI_APB_TRANS_FAIL) {97				dev_err(dev, "%s: transaction failed (0x%x)\n",98					__func__, status);99				return -EIO;100			}101			dev_dbg(dev, "%s: status 0x%x\n", __func__, status);102			return 0;103		}104		udelay(1);105	} while (timeout--);106 107	dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);108	return -ETIMEDOUT;109}110 111static int spmi_read_cmd(struct spmi_controller *ctrl,112			 u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)113{114	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);115	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;116	unsigned long flags;117	u8 *buf = __buf;118	u32 cmd, data;119	int rc;120	u8 op_code, i;121 122	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {123		dev_err(&ctrl->dev,124			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",125			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);126		return  -EINVAL;127	}128 129	switch (opc) {130	case SPMI_CMD_READ:131		op_code = SPMI_CMD_REG_READ;132		break;133	case SPMI_CMD_EXT_READ:134		op_code = SPMI_CMD_EXT_REG_READ;135		break;136	case SPMI_CMD_EXT_READL:137		op_code = SPMI_CMD_EXT_REG_READ_L;138		break;139	default:140		dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);141		return -EINVAL;142	}143 144	cmd = SPMI_APB_SPMI_CMD_EN |145	     (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |146	     ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |147	     ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */148	     ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */149 150	spin_lock_irqsave(&spmi_controller->lock, flags);151 152	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);153 154	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,155					   spmi_controller->base, slave_id, slave_addr);156	if (rc)157		goto done;158 159	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {160		data = readl(spmi_controller->base + chnl_ofst +161			     SPMI_SLAVE_OFFSET * slave_id +162			     SPMI_APB_SPMI_RDATA0_BASE_ADDR +163			     i * SPMI_PER_DATAREG_BYTE);164		data = be32_to_cpu((__be32 __force)data);165		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {166			memcpy(buf, &data, sizeof(data));167			buf += sizeof(data);168		} else {169			memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE);170			buf += (bc % SPMI_PER_DATAREG_BYTE);171		}172	}173 174done:175	spin_unlock_irqrestore(&spmi_controller->lock, flags);176	if (rc)177		dev_err(&ctrl->dev,178			"spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",179			opc, slave_id, slave_addr, bc + 1);180	else181		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n",182			__func__, slave_id, slave_addr, (int)bc, __buf);183 184	return rc;185}186 187static int spmi_write_cmd(struct spmi_controller *ctrl,188			  u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc)189{190	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);191	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;192	const u8 *buf = __buf;193	unsigned long flags;194	u32 cmd, data;195	int rc;196	u8 op_code, i;197 198	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {199		dev_err(&ctrl->dev,200			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",201			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);202		return  -EINVAL;203	}204 205	switch (opc) {206	case SPMI_CMD_WRITE:207		op_code = SPMI_CMD_REG_WRITE;208		break;209	case SPMI_CMD_EXT_WRITE:210		op_code = SPMI_CMD_EXT_REG_WRITE;211		break;212	case SPMI_CMD_EXT_WRITEL:213		op_code = SPMI_CMD_EXT_REG_WRITE_L;214		break;215	default:216		dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc);217		return -EINVAL;218	}219 220	cmd = SPMI_APB_SPMI_CMD_EN |221	      (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |222	      ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |223	      ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |224	      ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);225 226	/* Write data to FIFOs */227	spin_lock_irqsave(&spmi_controller->lock, flags);228 229	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {230		data = 0;231		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {232			memcpy(&data, buf, sizeof(data));233			buf += sizeof(data);234		} else {235			memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE);236			buf += (bc % SPMI_PER_DATAREG_BYTE);237		}238 239		writel((u32 __force)cpu_to_be32(data),240		       spmi_controller->base + chnl_ofst +241		       SPMI_APB_SPMI_WDATA0_BASE_ADDR +242		       SPMI_PER_DATAREG_BYTE * i);243	}244 245	/* Start the transaction */246	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);247 248	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,249					   spmi_controller->base, slave_id,250					   slave_addr);251	spin_unlock_irqrestore(&spmi_controller->lock, flags);252 253	if (rc)254		dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",255			opc, slave_id, slave_addr, bc);256	else257		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n",258			__func__, slave_id, slave_addr, (int)bc, __buf);259 260	return rc;261}262 263static int spmi_controller_probe(struct platform_device *pdev)264{265	struct spmi_controller_dev *spmi_controller;266	struct spmi_controller *ctrl;267	struct resource *iores;268	int ret;269 270	ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));271	if (IS_ERR(ctrl)) {272		dev_err(&pdev->dev, "can not allocate spmi_controller data\n");273		return PTR_ERR(ctrl);274	}275	spmi_controller = spmi_controller_get_drvdata(ctrl);276	spmi_controller->controller = ctrl;277 278	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);279	if (!iores) {280		dev_err(&pdev->dev, "can not get resource!\n");281		return -EINVAL;282	}283 284	spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,285					     resource_size(iores));286	if (!spmi_controller->base) {287		dev_err(&pdev->dev, "can not remap base addr!\n");288		return -EADDRNOTAVAIL;289	}290 291	ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",292				   &spmi_controller->channel);293	if (ret) {294		dev_err(&pdev->dev, "can not get channel\n");295		return -ENODEV;296	}297 298	platform_set_drvdata(pdev, spmi_controller);299	dev_set_drvdata(&ctrl->dev, spmi_controller);300 301	spin_lock_init(&spmi_controller->lock);302 303	ctrl->dev.parent = pdev->dev.parent;304	ctrl->dev.of_node = of_node_get(pdev->dev.of_node);305 306	/* Callbacks */307	ctrl->read_cmd = spmi_read_cmd;308	ctrl->write_cmd = spmi_write_cmd;309 310	ret = devm_spmi_controller_add(&pdev->dev, ctrl);311	if (ret) {312		dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);313		return ret;314	}315 316	return 0;317}318 319static const struct of_device_id spmi_controller_match_table[] = {320	{321		.compatible = "hisilicon,kirin970-spmi-controller",322	},323	{}324};325MODULE_DEVICE_TABLE(of, spmi_controller_match_table);326 327static struct platform_driver spmi_controller_driver = {328	.probe		= spmi_controller_probe,329	.driver		= {330		.name	= "hisi_spmi_controller",331		.of_match_table = spmi_controller_match_table,332	},333};334 335static int __init spmi_controller_init(void)336{337	return platform_driver_register(&spmi_controller_driver);338}339postcore_initcall(spmi_controller_init);340 341static void __exit spmi_controller_exit(void)342{343	platform_driver_unregister(&spmi_controller_driver);344}345module_exit(spmi_controller_exit);346 347MODULE_DESCRIPTION("Hisilicon 3670 SPMI Controller driver");348MODULE_LICENSE("GPL v2");349MODULE_VERSION("1.0");350MODULE_ALIAS("platform:spmi_controller");351