| 1 | |
| 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 <[email protected]>
|
| 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 |
|
| 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 |
|
| 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 |
|
| 77 | readl(spmi->regs + SPMI_RSP_REG);
|
| 78 |
|
| 79 |
|
| 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 |
|
| 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 <[email protected]>");
|
| 167 | MODULE_DESCRIPTION("Apple SoC SPMI driver");
|
| 168 | MODULE_LICENSE("GPL");
|