Skip to content

Commit 542dad1

Browse files
Jean-Francois Bortolottijannau
authored andcommitted
spmi: add a first basic spmi driver for Apple SoC
Signed-off-by: Jean-Francois Bortolotti <jeff@borto.fr>
1 parent 98f7e32 commit 542dad1

3 files changed

Lines changed: 231 additions & 0 deletions

File tree

drivers/spmi/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,12 @@ config SPMI_MTK_PMIF
4545
This is required for communicating with Mediatek PMICs and
4646
other devices that have the SPMI interface.
4747

48+
config SPMI_APPLE
49+
tristate "Apple SoC SPMI Controller platform driver"
50+
depends on ARCH_APPLE || COMPILE_TEST
51+
help
52+
This enables basic support for the SPMI controller present on
53+
many Apple SoCs, including the t8103 (M1) and t600x
54+
(M1 Pro/Max).
55+
4856
endif

drivers/spmi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ obj-$(CONFIG_SPMI) += spmi.o spmi-devres.o
77
obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o
88
obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o
99
obj-$(CONFIG_SPMI_MTK_PMIF) += spmi-mtk-pmif.o
10+
obj-$(CONFIG_SPMI_APPLE) += spmi-apple-controller.o
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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-ard.c Copyright (c) 2021, The Linux Foundation.
12+
*/
13+
14+
#include <linux/bits.h>
15+
#include <linux/delay.h>
16+
#include <linux/io.h>
17+
#include <linux/module.h>
18+
#include <linux/of.h>
19+
#include <linux/of_platform.h>
20+
#include <linux/platform_device.h>
21+
#include <linux/spmi.h>
22+
23+
/* SPMI Controller Registers */
24+
#define SPMI_STATUS_REG 0
25+
#define SPMI_CMD_REG 0x4
26+
#define SPMI_RSP_REG 0x8
27+
28+
#define SPMI_RX_FIFO_EMPTY BIT(24)
29+
#define SPMI_TX_FIFO_EMPTY BIT(8)
30+
31+
/* Apple SPMI controler */
32+
struct apple_spmi {
33+
void __iomem *regs;
34+
struct spmi_controller *ctrl;
35+
};
36+
37+
static inline u32 read_reg(struct apple_spmi *spmi, int offset)
38+
{
39+
return (readl_relaxed(spmi->regs + offset));
40+
}
41+
42+
static inline void write_reg(u32 value, struct apple_spmi *spmi, int offset)
43+
{
44+
writel_relaxed(value, spmi->regs + offset);
45+
}
46+
47+
static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 slave_id,
48+
u16 slave_addr, u8 *__buf, size_t bc)
49+
{
50+
struct apple_spmi *spmi;
51+
u32 spmi_cmd = opc | slave_id << 8 | slave_addr << 16 | (bc - 1) |
52+
(1 << 15);
53+
u32 rsp;
54+
volatile u32 status;
55+
size_t len_to_read;
56+
u8 i;
57+
58+
spmi = spmi_controller_get_drvdata(ctrl);
59+
60+
write_reg(spmi_cmd, spmi, SPMI_CMD_REG);
61+
62+
/* Wait for Rx FIFO to have something */
63+
/* Quite ugly msleep, need to find a better way to do it */
64+
i = 0;
65+
do {
66+
status = read_reg(spmi, SPMI_STATUS_REG);
67+
msleep(10);
68+
i += 1;
69+
} while ((status & SPMI_RX_FIFO_EMPTY) && i < 5);
70+
71+
if (i >= 5) {
72+
dev_err(&ctrl->dev,
73+
"spmi_read_cmd:took to long to get the status");
74+
return -1;
75+
}
76+
77+
/* Read SPMI reply status */
78+
rsp = read_reg(spmi, SPMI_RSP_REG);
79+
80+
len_to_read = 0;
81+
/* Read SPMI data reply */
82+
while (!(status & SPMI_RX_FIFO_EMPTY) && (len_to_read < bc)) {
83+
rsp = read_reg(spmi, SPMI_RSP_REG);
84+
i = 0;
85+
while ((len_to_read < bc) && (i < 4)) {
86+
__buf[len_to_read++] = ((0xff << (8 * i)) & rsp) >>
87+
(8 * i);
88+
i += 1;
89+
}
90+
}
91+
92+
return 0;
93+
}
94+
95+
static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 slave_id,
96+
u16 slave_addr, const u8 *__buf, size_t bc)
97+
{
98+
struct apple_spmi *spmi;
99+
u32 spmi_cmd = opc | slave_id << 8 | slave_addr << 16 | (bc - 1) |
100+
(1 << 15);
101+
volatile u32 rsp;
102+
volatile u32 status;
103+
size_t i = 0, j;
104+
105+
spmi = spmi_controller_get_drvdata(ctrl);
106+
107+
write_reg(spmi_cmd, spmi, SPMI_CMD_REG);
108+
109+
while (i < bc) {
110+
j = 0;
111+
spmi_cmd = 0;
112+
while ((j < 4) & (i < bc)) {
113+
spmi_cmd |= __buf[i++] << (j++ * 8);
114+
}
115+
write_reg(spmi_cmd, spmi, SPMI_CMD_REG);
116+
}
117+
118+
/* Wait for Rx FIFO to have something */
119+
/* Quite ugly msleep, need to find a better way to do it */
120+
i = 0;
121+
do {
122+
status = read_reg(spmi, SPMI_STATUS_REG);
123+
msleep(10);
124+
i += 1;
125+
} while ((status & SPMI_RX_FIFO_EMPTY) && i < 5);
126+
127+
if (i >= 5) {
128+
dev_err(&ctrl->dev,
129+
"spmi_write_cmd:took to long to get the status");
130+
return -1;
131+
}
132+
133+
rsp = read_reg(spmi, SPMI_RSP_REG);
134+
(void)rsp; // TODO: check stuff here
135+
136+
return 0;
137+
}
138+
139+
static int spmi_controller_probe(struct platform_device *pdev)
140+
{
141+
struct apple_spmi *spmi;
142+
struct spmi_controller *ctrl;
143+
int ret;
144+
145+
ctrl = spmi_controller_alloc(&pdev->dev, sizeof(struct apple_spmi));
146+
if (IS_ERR(ctrl)) {
147+
dev_err_probe(&pdev->dev, PTR_ERR(ctrl),
148+
"Can't allocate spmi_controller data\n");
149+
return -ENOMEM;
150+
}
151+
152+
spmi = spmi_controller_get_drvdata(ctrl);
153+
spmi->ctrl = ctrl;
154+
platform_set_drvdata(pdev, ctrl);
155+
156+
spmi->regs = devm_platform_ioremap_resource(pdev, 0);
157+
if (IS_ERR(spmi->regs)) {
158+
dev_err_probe(&pdev->dev, PTR_ERR(spmi->regs),
159+
"Can't get ioremap regs.\n");
160+
return PTR_ERR(spmi->regs);
161+
}
162+
163+
ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
164+
165+
/* Callbacks */
166+
ctrl->read_cmd = spmi_read_cmd;
167+
ctrl->write_cmd = spmi_write_cmd;
168+
169+
ret = spmi_controller_add(ctrl);
170+
if (ret) {
171+
dev_err(&pdev->dev,
172+
"spmi_controller_add failed with error %d!\n", ret);
173+
goto err_put_controller;
174+
}
175+
176+
/* Let's look for other nodes in device tree like the rtc */
177+
ret = devm_of_platform_populate(&pdev->dev);
178+
if (ret) {
179+
dev_err(&pdev->dev,
180+
"spmi_controller_probe: devm_of_platform_populate failed with error %d!\n",
181+
ret);
182+
goto err_devm_of_platform_populate;
183+
}
184+
185+
return 0;
186+
187+
err_put_controller:
188+
spmi_controller_put(ctrl);
189+
err_devm_of_platform_populate:
190+
return ret;
191+
}
192+
193+
static void spmi_del_controller(struct platform_device *pdev)
194+
{
195+
struct spmi_controller *ctrl = platform_get_drvdata(pdev);
196+
197+
spmi_controller_remove(ctrl);
198+
spmi_controller_put(ctrl);
199+
}
200+
201+
static const struct of_device_id spmi_controller_match_table[] = {
202+
{
203+
.compatible = "apple,spmi",
204+
},
205+
{}
206+
};
207+
MODULE_DEVICE_TABLE(of, spmi_controller_match_table);
208+
209+
static struct platform_driver spmi_controller_driver = {
210+
.probe = spmi_controller_probe,
211+
.remove = spmi_del_controller,
212+
.driver = {
213+
.name = "apple-spmi",
214+
.owner = THIS_MODULE,
215+
.of_match_table = spmi_controller_match_table,
216+
},
217+
};
218+
module_platform_driver(spmi_controller_driver);
219+
220+
MODULE_AUTHOR("Jean-Francois Bortolotti <jeff@borto.fr>");
221+
MODULE_DESCRIPTION("Apple SoC SPMI driver");
222+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)