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