xref: /linux/drivers/spmi/spmi-apple-controller.c (revision 995832b2cebe6969d1b42635db698803ee31294d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Apple SoC SPMI device driver
4  *
5  * Copyright The Asahi Linux Contributors
6  *
7  * Inspired by:
8  *		OpenBSD support Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
9  *		Correllium support Copyright (C) 2021 Corellium LLC
10  *		hisi-spmi-controller.c
11  *		spmi-pmic-arb.c Copyright (c) 2021, The Linux Foundation.
12  */
13 
14 #include <linux/io.h>
15 #include <linux/iopoll.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/spmi.h>
19 
20 /* SPMI Controller Registers */
21 #define SPMI_STATUS_REG 0
22 #define SPMI_CMD_REG 0x4
23 #define SPMI_RSP_REG 0x8
24 
25 #define SPMI_RX_FIFO_EMPTY BIT(24)
26 
27 #define REG_POLL_INTERVAL_US 10000
28 #define REG_POLL_TIMEOUT_US (REG_POLL_INTERVAL_US * 5)
29 
30 struct apple_spmi {
31 	void __iomem *regs;
32 };
33 
34 #define poll_reg(spmi, reg, val, cond) \
35 	readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \
36 			   REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US)
37 
38 static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 saddr, size_t len)
39 {
40 	return opc | sid << 8 | saddr << 16 | (len - 1) | (1 << 15);
41 }
42 
43 /* Wait for Rx FIFO to have something */
44 static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
45 {
46 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
47 	int ret;
48 	u32 status;
49 
50 	ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY));
51 	if (ret) {
52 		dev_err(&ctrl->dev,
53 			"failed to wait for RX FIFO not empty\n");
54 		return ret;
55 	}
56 
57 	return 0;
58 }
59 
60 static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
61 			 u16 saddr, u8 *buf, size_t len)
62 {
63 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
64 	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
65 	u32 rsp;
66 	size_t len_read = 0;
67 	u8 i;
68 	int ret;
69 
70 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
71 
72 	ret = apple_spmi_wait_rx_not_empty(ctrl);
73 	if (ret)
74 		return ret;
75 
76 	/* Discard SPMI reply status */
77 	readl(spmi->regs + SPMI_RSP_REG);
78 
79 	/* Read SPMI data reply */
80 	while (len_read < len) {
81 		rsp = readl(spmi->regs + SPMI_RSP_REG);
82 		i = 0;
83 		while ((len_read < len) && (i < 4)) {
84 			buf[len_read++] = ((0xff << (8 * i)) & rsp) >> (8 * i);
85 			i += 1;
86 		}
87 	}
88 
89 	return 0;
90 }
91 
92 static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
93 			  u16 saddr, const u8 *buf, size_t len)
94 {
95 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
96 	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
97 	size_t i = 0, j;
98 	int ret;
99 
100 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
101 
102 	while (i < len) {
103 		j = 0;
104 		spmi_cmd = 0;
105 		while ((j < 4) & (i < len))
106 			spmi_cmd |= buf[i++] << (j++ * 8);
107 
108 		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
109 	}
110 
111 	ret = apple_spmi_wait_rx_not_empty(ctrl);
112 	if (ret)
113 		return ret;
114 
115 	/* Discard */
116 	readl(spmi->regs + SPMI_RSP_REG);
117 
118 	return 0;
119 }
120 
121 static int apple_spmi_probe(struct platform_device *pdev)
122 {
123 	struct apple_spmi *spmi;
124 	struct spmi_controller *ctrl;
125 	int ret;
126 
127 	ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi));
128 	if (IS_ERR(ctrl))
129 		return -ENOMEM;
130 
131 	spmi = spmi_controller_get_drvdata(ctrl);
132 
133 	spmi->regs = devm_platform_ioremap_resource(pdev, 0);
134 	if (IS_ERR(spmi->regs))
135 		return PTR_ERR(spmi->regs);
136 
137 	ctrl->dev.of_node = pdev->dev.of_node;
138 
139 	ctrl->read_cmd = spmi_read_cmd;
140 	ctrl->write_cmd = spmi_write_cmd;
141 
142 	ret = devm_spmi_controller_add(&pdev->dev, ctrl);
143 	if (ret)
144 		return dev_err_probe(&pdev->dev, ret,
145 				     "spmi_controller_add failed\n");
146 
147 	return 0;
148 }
149 
150 static const struct of_device_id apple_spmi_match_table[] = {
151 	{ .compatible = "apple,t8103-spmi", },
152 	{ .compatible = "apple,spmi", },
153 	{}
154 };
155 MODULE_DEVICE_TABLE(of, apple_spmi_match_table);
156 
157 static struct platform_driver apple_spmi_driver = {
158 	.probe		= apple_spmi_probe,
159 	.driver		= {
160 		.name	= "apple-spmi",
161 		.of_match_table = apple_spmi_match_table,
162 	},
163 };
164 module_platform_driver(apple_spmi_driver);
165 
166 MODULE_AUTHOR("Jean-Francois Bortolotti <jeff@borto.fr>");
167 MODULE_DESCRIPTION("Apple SoC SPMI driver");
168 MODULE_LICENSE("GPL");
169