| 1 | |
| 2 | |
| 3 | * Apple SMC (System Management Controller) MFD driver
|
| 4 | *
|
| 5 | * Copyright The Asahi Linux Contributors
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/bitfield.h>
|
| 9 | #include <linux/delay.h>
|
| 10 | #include <linux/device.h>
|
| 11 | #include <linux/io.h>
|
| 12 | #include <linux/ioport.h>
|
| 13 | #include <linux/math.h>
|
| 14 | #include <linux/mfd/core.h>
|
| 15 | #include <linux/mfd/macsmc.h>
|
| 16 | #include <linux/notifier.h>
|
| 17 | #include <linux/of.h>
|
| 18 | #include <linux/of_platform.h>
|
| 19 | #include <linux/overflow.h>
|
| 20 | #include <linux/platform_device.h>
|
| 21 | #include <linux/soc/apple/rtkit.h>
|
| 22 | #include <linux/unaligned.h>
|
| 23 |
|
| 24 | #define SMC_ENDPOINT 0x20
|
| 25 |
|
| 26 |
|
| 27 | #define SMC_SHMEM_SIZE 0x1000
|
| 28 | #define SMC_MAX_SIZE 255
|
| 29 |
|
| 30 | #define SMC_MSG_READ_KEY 0x10
|
| 31 | #define SMC_MSG_WRITE_KEY 0x11
|
| 32 | #define SMC_MSG_GET_KEY_BY_INDEX 0x12
|
| 33 | #define SMC_MSG_GET_KEY_INFO 0x13
|
| 34 | #define SMC_MSG_INITIALIZE 0x17
|
| 35 | #define SMC_MSG_NOTIFICATION 0x18
|
| 36 | #define SMC_MSG_RW_KEY 0x20
|
| 37 |
|
| 38 | #define SMC_DATA GENMASK_ULL(63, 32)
|
| 39 | #define SMC_WSIZE GENMASK_ULL(31, 24)
|
| 40 | #define SMC_SIZE GENMASK_ULL(23, 16)
|
| 41 | #define SMC_ID GENMASK_ULL(15, 12)
|
| 42 | #define SMC_MSG GENMASK_ULL(7, 0)
|
| 43 | #define SMC_RESULT SMC_MSG
|
| 44 |
|
| 45 | #define SMC_TIMEOUT_MS 500
|
| 46 |
|
| 47 | static const struct mfd_cell apple_smc_devs[] = {
|
| 48 | MFD_CELL_NAME("macsmc-input"),
|
| 49 | MFD_CELL_NAME("macsmc-power"),
|
| 50 | MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
|
| 51 | MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"),
|
| 52 | MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
|
| 53 | MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"),
|
| 54 | };
|
| 55 |
|
| 56 | static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,
|
| 57 | u64 size, u64 wsize, u32 *ret_data)
|
| 58 | {
|
| 59 | u8 result;
|
| 60 | int ret;
|
| 61 | u64 msg;
|
| 62 |
|
| 63 | lockdep_assert_held(&smc->mutex);
|
| 64 |
|
| 65 | if (smc->boot_stage != APPLE_SMC_INITIALIZED)
|
| 66 | return -EIO;
|
| 67 | if (smc->atomic_mode)
|
| 68 | return -EIO;
|
| 69 |
|
| 70 | reinit_completion(&smc->cmd_done);
|
| 71 |
|
| 72 | smc->msg_id = (smc->msg_id + 1) & 0xf;
|
| 73 | msg = (FIELD_PREP(SMC_MSG, cmd) |
|
| 74 | FIELD_PREP(SMC_SIZE, size) |
|
| 75 | FIELD_PREP(SMC_WSIZE, wsize) |
|
| 76 | FIELD_PREP(SMC_ID, smc->msg_id) |
|
| 77 | FIELD_PREP(SMC_DATA, arg));
|
| 78 |
|
| 79 | ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, false);
|
| 80 | if (ret) {
|
| 81 | dev_err(smc->dev, "Failed to send command\n");
|
| 82 | return ret;
|
| 83 | }
|
| 84 |
|
| 85 | if (wait_for_completion_timeout(&smc->cmd_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) <= 0) {
|
| 86 | dev_err(smc->dev, "Command timed out (%llx)", msg);
|
| 87 | return -ETIMEDOUT;
|
| 88 | }
|
| 89 |
|
| 90 | if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
|
| 91 | dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
|
| 92 | smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
|
| 93 | return -EIO;
|
| 94 | }
|
| 95 |
|
| 96 | result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
|
| 97 | if (result)
|
| 98 | return -EIO;
|
| 99 |
|
| 100 | if (ret_data)
|
| 101 | *ret_data = FIELD_GET(SMC_DATA, smc->cmd_ret);
|
| 102 |
|
| 103 | return FIELD_GET(SMC_SIZE, smc->cmd_ret);
|
| 104 | }
|
| 105 |
|
| 106 | static int apple_smc_cmd(struct apple_smc *smc, u64 cmd, u64 arg,
|
| 107 | u64 size, u64 wsize, u32 *ret_data)
|
| 108 | {
|
| 109 | guard(mutex)(&smc->mutex);
|
| 110 |
|
| 111 | return apple_smc_cmd_locked(smc, cmd, arg, size, wsize, ret_data);
|
| 112 | }
|
| 113 |
|
| 114 | static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
|
| 115 | const void *wbuf, size_t wsize,
|
| 116 | void *rbuf, size_t rsize)
|
| 117 | {
|
| 118 | u64 smc_size, smc_wsize;
|
| 119 | u32 rdata;
|
| 120 | int ret;
|
| 121 | u64 cmd;
|
| 122 |
|
| 123 | lockdep_assert_held(&smc->mutex);
|
| 124 |
|
| 125 | if (rsize > SMC_MAX_SIZE)
|
| 126 | return -EINVAL;
|
| 127 | if (wsize > SMC_MAX_SIZE)
|
| 128 | return -EINVAL;
|
| 129 |
|
| 130 | if (rsize && wsize) {
|
| 131 | cmd = SMC_MSG_RW_KEY;
|
| 132 | memcpy_toio(smc->shmem.iomem, wbuf, wsize);
|
| 133 | smc_size = rsize;
|
| 134 | smc_wsize = wsize;
|
| 135 | } else if (wsize && !rsize) {
|
| 136 | cmd = SMC_MSG_WRITE_KEY;
|
| 137 | memcpy_toio(smc->shmem.iomem, wbuf, wsize);
|
| 138 | |
| 139 | * Setting size to the length we want to write and wsize to 0
|
| 140 | * looks silly but that's how the SMC protocol works ¯\_(ツ)_/¯
|
| 141 |
|
| 142 | smc_size = wsize;
|
| 143 | smc_wsize = 0;
|
| 144 | } else if (!wsize && rsize) {
|
| 145 | cmd = SMC_MSG_READ_KEY;
|
| 146 | smc_size = rsize;
|
| 147 | smc_wsize = 0;
|
| 148 | } else {
|
| 149 | return -EINVAL;
|
| 150 | }
|
| 151 |
|
| 152 | ret = apple_smc_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata);
|
| 153 | if (ret < 0)
|
| 154 | return ret;
|
| 155 |
|
| 156 | if (rsize) {
|
| 157 | |
| 158 | * Small data <= 4 bytes is returned as part of the reply
|
| 159 | * message which is sent over the mailbox FIFO. Everything
|
| 160 | * bigger has to be copied from SRAM which is mapped as
|
| 161 | * Device memory.
|
| 162 |
|
| 163 | if (rsize <= 4)
|
| 164 | memcpy(rbuf, &rdata, rsize);
|
| 165 | else
|
| 166 | memcpy_fromio(rbuf, smc->shmem.iomem, rsize);
|
| 167 | }
|
| 168 |
|
| 169 | return ret;
|
| 170 | }
|
| 171 |
|
| 172 | int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
|
| 173 | {
|
| 174 | guard(mutex)(&smc->mutex);
|
| 175 |
|
| 176 | return apple_smc_rw_locked(smc, key, NULL, 0, buf, size);
|
| 177 | }
|
| 178 | EXPORT_SYMBOL(apple_smc_read);
|
| 179 |
|
| 180 | int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
|
| 181 | {
|
| 182 | guard(mutex)(&smc->mutex);
|
| 183 |
|
| 184 | return apple_smc_rw_locked(smc, key, buf, size, NULL, 0);
|
| 185 | }
|
| 186 | EXPORT_SYMBOL(apple_smc_write);
|
| 187 |
|
| 188 | int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
|
| 189 | void *rbuf, size_t rsize)
|
| 190 | {
|
| 191 | guard(mutex)(&smc->mutex);
|
| 192 |
|
| 193 | return apple_smc_rw_locked(smc, key, wbuf, wsize, rbuf, rsize);
|
| 194 | }
|
| 195 | EXPORT_SYMBOL(apple_smc_rw);
|
| 196 |
|
| 197 | int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
|
| 198 | {
|
| 199 | int ret;
|
| 200 |
|
| 201 | ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key);
|
| 202 |
|
| 203 | *key = swab32(*key);
|
| 204 | return ret;
|
| 205 | }
|
| 206 | EXPORT_SYMBOL(apple_smc_get_key_by_index);
|
| 207 |
|
| 208 | int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
|
| 209 | {
|
| 210 | u8 key_info[6];
|
| 211 | int ret;
|
| 212 |
|
| 213 | ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL);
|
| 214 | if (ret >= 0 && info) {
|
| 215 | memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info));
|
| 216 | info->size = key_info[0];
|
| 217 | info->type_code = get_unaligned_be32(&key_info[1]);
|
| 218 | info->flags = key_info[5];
|
| 219 | }
|
| 220 | return ret;
|
| 221 | }
|
| 222 | EXPORT_SYMBOL(apple_smc_get_key_info);
|
| 223 |
|
| 224 | int apple_smc_enter_atomic(struct apple_smc *smc)
|
| 225 | {
|
| 226 | guard(mutex)(&smc->mutex);
|
| 227 |
|
| 228 | |
| 229 | * Disable notifications since this is called before shutdown and no
|
| 230 | * notification handler will be able to handle the notification
|
| 231 | * using atomic operations only. Also ignore any failure here
|
| 232 | * because we're about to shut down or reboot anyway.
|
| 233 | * We can't use apple_smc_write_flag here since that would try to lock
|
| 234 | * smc->mutex again.
|
| 235 |
|
| 236 | const u8 flag = 0;
|
| 237 |
|
| 238 | apple_smc_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
|
| 239 |
|
| 240 | smc->atomic_mode = true;
|
| 241 |
|
| 242 | return 0;
|
| 243 | }
|
| 244 | EXPORT_SYMBOL(apple_smc_enter_atomic);
|
| 245 |
|
| 246 | int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
|
| 247 | {
|
| 248 | guard(spinlock_irqsave)(&smc->lock);
|
| 249 | u8 result;
|
| 250 | int ret;
|
| 251 | u64 msg;
|
| 252 |
|
| 253 | if (size > SMC_MAX_SIZE || size == 0)
|
| 254 | return -EINVAL;
|
| 255 |
|
| 256 | if (smc->boot_stage != APPLE_SMC_INITIALIZED)
|
| 257 | return -EIO;
|
| 258 | if (!smc->atomic_mode)
|
| 259 | return -EIO;
|
| 260 |
|
| 261 | memcpy_toio(smc->shmem.iomem, buf, size);
|
| 262 | smc->msg_id = (smc->msg_id + 1) & 0xf;
|
| 263 | msg = (FIELD_PREP(SMC_MSG, SMC_MSG_WRITE_KEY) |
|
| 264 | FIELD_PREP(SMC_SIZE, size) |
|
| 265 | FIELD_PREP(SMC_ID, smc->msg_id) |
|
| 266 | FIELD_PREP(SMC_DATA, key));
|
| 267 | smc->atomic_pending = true;
|
| 268 |
|
| 269 | ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, true);
|
| 270 | if (ret < 0) {
|
| 271 | dev_err(smc->dev, "Failed to send command (%d)\n", ret);
|
| 272 | return ret;
|
| 273 | }
|
| 274 |
|
| 275 | while (smc->atomic_pending) {
|
| 276 | ret = apple_rtkit_poll(smc->rtk);
|
| 277 | if (ret < 0) {
|
| 278 | dev_err(smc->dev, "RTKit poll failed (%llx)", msg);
|
| 279 | return ret;
|
| 280 | }
|
| 281 | udelay(100);
|
| 282 | }
|
| 283 |
|
| 284 | if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
|
| 285 | dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
|
| 286 | smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
|
| 287 | return -EIO;
|
| 288 | }
|
| 289 |
|
| 290 | result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
|
| 291 | if (result)
|
| 292 | return -EIO;
|
| 293 |
|
| 294 | return FIELD_GET(SMC_SIZE, smc->cmd_ret);
|
| 295 | }
|
| 296 | EXPORT_SYMBOL(apple_smc_write_atomic);
|
| 297 |
|
| 298 | static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_len)
|
| 299 | {
|
| 300 | struct apple_smc *smc = cookie;
|
| 301 |
|
| 302 | smc->boot_stage = APPLE_SMC_ERROR_CRASHED;
|
| 303 | dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n");
|
| 304 | }
|
| 305 |
|
| 306 | static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
|
| 307 | {
|
| 308 | struct apple_smc *smc = cookie;
|
| 309 | size_t bfr_end;
|
| 310 |
|
| 311 | if (!bfr->iova) {
|
| 312 | dev_err(smc->dev, "RTKit wants a RAM buffer\n");
|
| 313 | return -EIO;
|
| 314 | }
|
| 315 |
|
| 316 | if (check_add_overflow(bfr->iova, bfr->size - 1, &bfr_end))
|
| 317 | return -EFAULT;
|
| 318 |
|
| 319 | if (bfr->iova < smc->sram->start || bfr->iova > smc->sram->end ||
|
| 320 | bfr_end > smc->sram->end) {
|
| 321 | dev_err(smc->dev, "RTKit buffer request outside SRAM region: [0x%llx, 0x%llx]\n",
|
| 322 | (unsigned long long)bfr->iova,
|
| 323 | (unsigned long long)bfr_end);
|
| 324 | return -EFAULT;
|
| 325 | }
|
| 326 |
|
| 327 | bfr->iomem = smc->sram_base + (bfr->iova - smc->sram->start);
|
| 328 | bfr->is_mapped = true;
|
| 329 |
|
| 330 | return 0;
|
| 331 | }
|
| 332 |
|
| 333 | static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message)
|
| 334 | {
|
| 335 | struct apple_smc *smc = cookie;
|
| 336 |
|
| 337 | if (endpoint != SMC_ENDPOINT) {
|
| 338 | dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
|
| 339 | return false;
|
| 340 | }
|
| 341 |
|
| 342 | if (smc->boot_stage == APPLE_SMC_BOOTING) {
|
| 343 | int ret;
|
| 344 |
|
| 345 | smc->shmem.iova = message;
|
| 346 | smc->shmem.size = SMC_SHMEM_SIZE;
|
| 347 | ret = apple_smc_rtkit_shmem_setup(smc, &smc->shmem);
|
| 348 | if (ret < 0) {
|
| 349 | smc->boot_stage = APPLE_SMC_ERROR_NO_SHMEM;
|
| 350 | dev_err(smc->dev, "Failed to initialize shared memory (%d)\n", ret);
|
| 351 | } else {
|
| 352 | smc->boot_stage = APPLE_SMC_INITIALIZED;
|
| 353 | }
|
| 354 | complete(&smc->init_done);
|
| 355 | } else if (FIELD_GET(SMC_MSG, message) == SMC_MSG_NOTIFICATION) {
|
| 356 |
|
| 357 | return false;
|
| 358 | } else {
|
| 359 | smc->cmd_ret = message;
|
| 360 | if (smc->atomic_pending)
|
| 361 | smc->atomic_pending = false;
|
| 362 | else
|
| 363 | complete(&smc->cmd_done);
|
| 364 | }
|
| 365 |
|
| 366 | return true;
|
| 367 | }
|
| 368 |
|
| 369 | static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message)
|
| 370 | {
|
| 371 | struct apple_smc *smc = cookie;
|
| 372 |
|
| 373 | if (endpoint != SMC_ENDPOINT) {
|
| 374 | dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
|
| 375 | return;
|
| 376 | }
|
| 377 |
|
| 378 | if (FIELD_GET(SMC_MSG, message) != SMC_MSG_NOTIFICATION) {
|
| 379 | dev_warn(smc->dev, "Received unknown message from worker: 0x%llx\n", message);
|
| 380 | return;
|
| 381 | }
|
| 382 |
|
| 383 | blocking_notifier_call_chain(&smc->event_handlers, FIELD_GET(SMC_DATA, message), NULL);
|
| 384 | }
|
| 385 |
|
| 386 | static const struct apple_rtkit_ops apple_smc_rtkit_ops = {
|
| 387 | .crashed = apple_smc_rtkit_crashed,
|
| 388 | .recv_message = apple_smc_rtkit_recv,
|
| 389 | .recv_message_early = apple_smc_rtkit_recv_early,
|
| 390 | .shmem_setup = apple_smc_rtkit_shmem_setup,
|
| 391 | };
|
| 392 |
|
| 393 | static void apple_smc_rtkit_shutdown(void *data)
|
| 394 | {
|
| 395 | struct apple_smc *smc = data;
|
| 396 |
|
| 397 |
|
| 398 | if (apple_rtkit_is_running(smc->rtk))
|
| 399 | apple_rtkit_quiesce(smc->rtk);
|
| 400 | }
|
| 401 |
|
| 402 | static void apple_smc_disable_notifications(void *data)
|
| 403 | {
|
| 404 | struct apple_smc *smc = data;
|
| 405 |
|
| 406 | apple_smc_write_flag(smc, SMC_KEY(NTAP), false);
|
| 407 | }
|
| 408 |
|
| 409 | static int apple_smc_probe(struct platform_device *pdev)
|
| 410 | {
|
| 411 | struct device *dev = &pdev->dev;
|
| 412 | struct apple_smc *smc;
|
| 413 | __be32 count;
|
| 414 | int ret;
|
| 415 |
|
| 416 | smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
|
| 417 | if (!smc)
|
| 418 | return -ENOMEM;
|
| 419 |
|
| 420 | mutex_init(&smc->mutex);
|
| 421 | smc->dev = &pdev->dev;
|
| 422 | smc->sram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &smc->sram);
|
| 423 | if (IS_ERR(smc->sram_base))
|
| 424 | return dev_err_probe(dev, PTR_ERR(smc->sram_base), "Failed to map SRAM region");
|
| 425 |
|
| 426 | smc->rtk = devm_apple_rtkit_init(dev, smc, NULL, 0, &apple_smc_rtkit_ops);
|
| 427 | if (IS_ERR(smc->rtk))
|
| 428 | return dev_err_probe(dev, PTR_ERR(smc->rtk), "Failed to initialize RTKit");
|
| 429 |
|
| 430 | smc->boot_stage = APPLE_SMC_BOOTING;
|
| 431 | ret = apple_rtkit_wake(smc->rtk);
|
| 432 | if (ret)
|
| 433 | return dev_err_probe(dev, ret, "Failed to wake up SMC");
|
| 434 |
|
| 435 | ret = devm_add_action_or_reset(dev, apple_smc_rtkit_shutdown, smc);
|
| 436 | if (ret)
|
| 437 | return ret;
|
| 438 |
|
| 439 | ret = apple_rtkit_start_ep(smc->rtk, SMC_ENDPOINT);
|
| 440 | if (ret)
|
| 441 | return dev_err_probe(dev, ret, "Failed to start SMC endpoint");
|
| 442 |
|
| 443 | init_completion(&smc->init_done);
|
| 444 | init_completion(&smc->cmd_done);
|
| 445 |
|
| 446 | ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT,
|
| 447 | FIELD_PREP(SMC_MSG, SMC_MSG_INITIALIZE), NULL, false);
|
| 448 | if (ret)
|
| 449 | return dev_err_probe(dev, ret, "Failed to send init message");
|
| 450 |
|
| 451 | if (wait_for_completion_timeout(&smc->init_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) == 0) {
|
| 452 | dev_err(dev, "Timed out initializing SMC");
|
| 453 | return -ETIMEDOUT;
|
| 454 | }
|
| 455 |
|
| 456 | if (smc->boot_stage != APPLE_SMC_INITIALIZED) {
|
| 457 | dev_err(dev, "SMC failed to boot successfully, boot stage=%d\n", smc->boot_stage);
|
| 458 | return -EIO;
|
| 459 | }
|
| 460 |
|
| 461 | dev_set_drvdata(&pdev->dev, smc);
|
| 462 | BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
|
| 463 |
|
| 464 | ret = apple_smc_read(smc, SMC_KEY(#KEY), &count, sizeof(count));
|
| 465 | if (ret >= 0 && ret != sizeof(count))
|
| 466 | ret = -EINVAL;
|
| 467 | if (ret < 0)
|
| 468 | return dev_err_probe(smc->dev, ret, "Failed to get key count");
|
| 469 | smc->key_count = be32_to_cpu(count);
|
| 470 |
|
| 471 |
|
| 472 | apple_smc_write_flag(smc, SMC_KEY(NTAP), true);
|
| 473 | ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc);
|
| 474 | if (ret)
|
| 475 | return ret;
|
| 476 |
|
| 477 | ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
|
| 478 | apple_smc_devs, ARRAY_SIZE(apple_smc_devs),
|
| 479 | NULL, 0, NULL);
|
| 480 | if (ret)
|
| 481 | return dev_err_probe(smc->dev, ret, "Failed to register sub-devices");
|
| 482 |
|
| 483 |
|
| 484 | return 0;
|
| 485 | }
|
| 486 |
|
| 487 | static const struct of_device_id apple_smc_of_match[] = {
|
| 488 | { .compatible = "apple,t8103-smc" },
|
| 489 | { .compatible = "apple,smc" },
|
| 490 | {},
|
| 491 | };
|
| 492 | MODULE_DEVICE_TABLE(of, apple_smc_of_match);
|
| 493 |
|
| 494 | static struct platform_driver apple_smc_driver = {
|
| 495 | .driver = {
|
| 496 | .name = "macsmc",
|
| 497 | .of_match_table = apple_smc_of_match,
|
| 498 | },
|
| 499 | .probe = apple_smc_probe,
|
| 500 | };
|
| 501 | module_platform_driver(apple_smc_driver);
|
| 502 |
|
| 503 | MODULE_AUTHOR("Hector Martin <[email protected]>");
|
| 504 | MODULE_AUTHOR("Sven Peter <[email protected]>");
|
| 505 | MODULE_LICENSE("Dual MIT/GPL");
|
| 506 | MODULE_DESCRIPTION("Apple SMC driver");
|