^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Driver for Khadas System control Microcontroller
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2020 BayLibre SAS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author(s): Neil Armstrong <narmstrong@baylibre.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/bitfield.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/i2c.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/mfd/core.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/mfd/khadas-mcu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) if (reg >= KHADAS_MCU_USER_DATA_0_REG &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) reg < KHADAS_MCU_PWR_OFF_CMD_REG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) switch (reg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) case KHADAS_MCU_PWR_OFF_CMD_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) case KHADAS_MCU_PASSWD_START_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) case KHADAS_MCU_CHECK_VEN_PASSWD_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) case KHADAS_MCU_CHECK_USER_PASSWD_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) case KHADAS_MCU_WOL_INIT_START_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) switch (reg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) case KHADAS_MCU_PASSWD_VEN_0_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) case KHADAS_MCU_PASSWD_VEN_1_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) case KHADAS_MCU_PASSWD_VEN_2_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) case KHADAS_MCU_PASSWD_VEN_3_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) case KHADAS_MCU_PASSWD_VEN_4_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) case KHADAS_MCU_PASSWD_VEN_5_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) case KHADAS_MCU_MAC_0_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) case KHADAS_MCU_MAC_1_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) case KHADAS_MCU_MAC_2_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) case KHADAS_MCU_MAC_3_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) case KHADAS_MCU_MAC_4_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) case KHADAS_MCU_MAC_5_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) case KHADAS_MCU_USID_0_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) case KHADAS_MCU_USID_1_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) case KHADAS_MCU_USID_2_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) case KHADAS_MCU_USID_3_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) case KHADAS_MCU_USID_4_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) case KHADAS_MCU_USID_5_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) case KHADAS_MCU_VERSION_0_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) case KHADAS_MCU_VERSION_1_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) case KHADAS_MCU_DEVICE_NO_0_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) case KHADAS_MCU_DEVICE_NO_1_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) case KHADAS_MCU_FACTORY_TEST_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static const struct regmap_config khadas_mcu_regmap_config = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) .reg_bits = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) .reg_stride = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .val_bits = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .volatile_reg = khadas_mcu_reg_volatile,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .writeable_reg = khadas_mcu_reg_writeable,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) .cache_type = REGCACHE_RBTREE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static struct mfd_cell khadas_mcu_fan_cells[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) /* VIM1/2 Rev13+ and VIM3 only */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) { .name = "khadas-mcu-fan-ctrl", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static struct mfd_cell khadas_mcu_cells[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) { .name = "khadas-mcu-user-mem", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) static int khadas_mcu_probe(struct i2c_client *client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) const struct i2c_device_id *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct device *dev = &client->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct khadas_mcu *ddata;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (!ddata)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) i2c_set_clientdata(client, ddata);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) ddata->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) if (IS_ERR(ddata->regmap)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) ret = PTR_ERR(ddata->regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) dev_err(dev, "Failed to allocate register map: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) khadas_mcu_cells,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) ARRAY_SIZE(khadas_mcu_cells),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) NULL, 0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (of_find_property(dev->of_node, "#cooling-cells", NULL))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) khadas_mcu_fan_cells,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) ARRAY_SIZE(khadas_mcu_fan_cells),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) NULL, 0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) #ifdef CONFIG_OF
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static const struct of_device_id khadas_mcu_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) { .compatible = "khadas,mcu", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static struct i2c_driver khadas_mcu_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .name = "khadas-mcu-core",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .of_match_table = of_match_ptr(khadas_mcu_of_match),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .probe = khadas_mcu_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) module_i2c_driver(khadas_mcu_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) MODULE_DESCRIPTION("Khadas MCU core driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) MODULE_LICENSE("GPL v2");