0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "meson-sm: " fmt
0010
0011 #include <linux/arm-smccc.h>
0012 #include <linux/bug.h>
0013 #include <linux/io.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/of_device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/printk.h>
0019 #include <linux/types.h>
0020 #include <linux/sizes.h>
0021 #include <linux/slab.h>
0022
0023 #include <linux/firmware/meson/meson_sm.h>
0024
0025 struct meson_sm_cmd {
0026 unsigned int index;
0027 u32 smc_id;
0028 };
0029 #define CMD(d, s) { .index = (d), .smc_id = (s), }
0030
0031 struct meson_sm_chip {
0032 unsigned int shmem_size;
0033 u32 cmd_shmem_in_base;
0034 u32 cmd_shmem_out_base;
0035 struct meson_sm_cmd cmd[];
0036 };
0037
0038 static const struct meson_sm_chip gxbb_chip = {
0039 .shmem_size = SZ_4K,
0040 .cmd_shmem_in_base = 0x82000020,
0041 .cmd_shmem_out_base = 0x82000021,
0042 .cmd = {
0043 CMD(SM_EFUSE_READ, 0x82000030),
0044 CMD(SM_EFUSE_WRITE, 0x82000031),
0045 CMD(SM_EFUSE_USER_MAX, 0x82000033),
0046 CMD(SM_GET_CHIP_ID, 0x82000044),
0047 CMD(SM_A1_PWRC_SET, 0x82000093),
0048 CMD(SM_A1_PWRC_GET, 0x82000095),
0049 { },
0050 },
0051 };
0052
0053 struct meson_sm_firmware {
0054 const struct meson_sm_chip *chip;
0055 void __iomem *sm_shmem_in_base;
0056 void __iomem *sm_shmem_out_base;
0057 };
0058
0059 static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
0060 unsigned int cmd_index)
0061 {
0062 const struct meson_sm_cmd *cmd = chip->cmd;
0063
0064 while (cmd->smc_id && cmd->index != cmd_index)
0065 cmd++;
0066
0067 return cmd->smc_id;
0068 }
0069
0070 static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
0071 u32 arg3, u32 arg4)
0072 {
0073 struct arm_smccc_res res;
0074
0075 arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res);
0076 return res.a0;
0077 }
0078
0079 static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
0080 {
0081 u32 sm_phy_base;
0082
0083 sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0);
0084 if (!sm_phy_base)
0085 return 0;
0086
0087 return ioremap_cache(sm_phy_base, size);
0088 }
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104 int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index,
0105 u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
0106 {
0107 u32 cmd, lret;
0108
0109 if (!fw->chip)
0110 return -ENOENT;
0111
0112 cmd = meson_sm_get_cmd(fw->chip, cmd_index);
0113 if (!cmd)
0114 return -EINVAL;
0115
0116 lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4);
0117
0118 if (ret)
0119 *ret = lret;
0120
0121 return 0;
0122 }
0123 EXPORT_SYMBOL(meson_sm_call);
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142 int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
0143 unsigned int bsize, unsigned int cmd_index, u32 arg0,
0144 u32 arg1, u32 arg2, u32 arg3, u32 arg4)
0145 {
0146 u32 size;
0147 int ret;
0148
0149 if (!fw->chip)
0150 return -ENOENT;
0151
0152 if (!fw->chip->cmd_shmem_out_base)
0153 return -EINVAL;
0154
0155 if (bsize > fw->chip->shmem_size)
0156 return -EINVAL;
0157
0158 if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
0159 return -EINVAL;
0160
0161 if (size > bsize)
0162 return -EINVAL;
0163
0164 ret = size;
0165
0166 if (!size)
0167 size = bsize;
0168
0169 if (buffer)
0170 memcpy(buffer, fw->sm_shmem_out_base, size);
0171
0172 return ret;
0173 }
0174 EXPORT_SYMBOL(meson_sm_call_read);
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191 int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
0192 unsigned int size, unsigned int cmd_index, u32 arg0,
0193 u32 arg1, u32 arg2, u32 arg3, u32 arg4)
0194 {
0195 u32 written;
0196
0197 if (!fw->chip)
0198 return -ENOENT;
0199
0200 if (size > fw->chip->shmem_size)
0201 return -EINVAL;
0202
0203 if (!fw->chip->cmd_shmem_in_base)
0204 return -EINVAL;
0205
0206 memcpy(fw->sm_shmem_in_base, buffer, size);
0207
0208 if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
0209 return -EINVAL;
0210
0211 if (!written)
0212 return -EINVAL;
0213
0214 return written;
0215 }
0216 EXPORT_SYMBOL(meson_sm_call_write);
0217
0218
0219
0220
0221
0222
0223
0224
0225 struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node)
0226 {
0227 struct platform_device *pdev = of_find_device_by_node(sm_node);
0228
0229 if (!pdev)
0230 return NULL;
0231
0232 return platform_get_drvdata(pdev);
0233 }
0234 EXPORT_SYMBOL_GPL(meson_sm_get);
0235
0236 #define SM_CHIP_ID_LENGTH 119
0237 #define SM_CHIP_ID_OFFSET 4
0238 #define SM_CHIP_ID_SIZE 12
0239
0240 static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
0241 char *buf)
0242 {
0243 struct platform_device *pdev = to_platform_device(dev);
0244 struct meson_sm_firmware *fw;
0245 uint8_t *id_buf;
0246 int ret;
0247
0248 fw = platform_get_drvdata(pdev);
0249
0250 id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
0251 if (!id_buf)
0252 return -ENOMEM;
0253
0254 ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
0255 0, 0, 0, 0, 0);
0256 if (ret < 0) {
0257 kfree(id_buf);
0258 return ret;
0259 }
0260
0261 ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]);
0262
0263 kfree(id_buf);
0264
0265 return ret;
0266 }
0267
0268 static DEVICE_ATTR_RO(serial);
0269
0270 static struct attribute *meson_sm_sysfs_attributes[] = {
0271 &dev_attr_serial.attr,
0272 NULL,
0273 };
0274
0275 static const struct attribute_group meson_sm_sysfs_attr_group = {
0276 .attrs = meson_sm_sysfs_attributes,
0277 };
0278
0279 static const struct of_device_id meson_sm_ids[] = {
0280 { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
0281 { },
0282 };
0283
0284 static int __init meson_sm_probe(struct platform_device *pdev)
0285 {
0286 struct device *dev = &pdev->dev;
0287 const struct meson_sm_chip *chip;
0288 struct meson_sm_firmware *fw;
0289
0290 fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
0291 if (!fw)
0292 return -ENOMEM;
0293
0294 chip = of_match_device(meson_sm_ids, dev)->data;
0295
0296 if (chip->cmd_shmem_in_base) {
0297 fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
0298 chip->shmem_size);
0299 if (WARN_ON(!fw->sm_shmem_in_base))
0300 goto out;
0301 }
0302
0303 if (chip->cmd_shmem_out_base) {
0304 fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
0305 chip->shmem_size);
0306 if (WARN_ON(!fw->sm_shmem_out_base))
0307 goto out_in_base;
0308 }
0309
0310 fw->chip = chip;
0311
0312 platform_set_drvdata(pdev, fw);
0313
0314 pr_info("secure-monitor enabled\n");
0315
0316 if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
0317 goto out_in_base;
0318
0319 return 0;
0320
0321 out_in_base:
0322 iounmap(fw->sm_shmem_in_base);
0323 out:
0324 return -EINVAL;
0325 }
0326
0327 static struct platform_driver meson_sm_driver = {
0328 .driver = {
0329 .name = "meson-sm",
0330 .of_match_table = of_match_ptr(meson_sm_ids),
0331 },
0332 };
0333 module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
0334 MODULE_LICENSE("GPL v2");