// SPDX-License-Identifier: GPL-2.0+ /* * Some functions are derived from: * https://github.com/rockchip-linux/u-boot/blob/next-dev/drivers/rknand/rk_ftl_arm_v7.S * Copyright (c) 2016-2018, Fuzhou Rockchip Electronics Co., Ltd * * Driver interface derived from: * /drivers/block/host_dev.c * /drivers/block/host-uclass.c * Copyright 2022 Google LLC * Written by Simon Glass * * Copyright (C) 2023 Johan Jonker */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !IS_ENABLED(CONFIG_SANDBOX) #include #endif #include struct nand_para_info nand_para_tbl[] = { {6, {0x2c, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x44, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 1064, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 4, 1, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x88, 0x04, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0xa8, 0x05, 0xcb, 0xa9, 0x00}, 4, 2, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x68, 0x04, 0x46, 0x89, 0x00}, 4, 1, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x48, 0x04, 0x4a, 0xa5, 0x00}, 4, 1, 8, 256, 2, 2, 1024, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x64, 0x54, 0xa9, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 4, 1, 8, 128, 2, 2, 4096, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x48, 0x04, 0x46, 0x85, 0x00}, 4, 1, 8, 256, 2, 2, 1024, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x88, 0x05, 0xc6, 0x89, 0x00}, 4, 2, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x68, 0x00, 0x27, 0xa9, 0x00}, 4, 1, 16, 128, 1, 2, 2048, 0x011f, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x64, 0x64, 0x56, 0xa5, 0x00}, 4, 1, 24, 512, 2, 2, 700, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0x84, 0xc5, 0x4b, 0xa9, 0x00}, 4, 2, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0xd5, 0xd1, 0xa6, 0x68, 0x00}, 4, 2, 8, 64, 1, 2, 2048, 0x0117, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0xdc, 0x90, 0xa6, 0x54, 0x00}, 4, 1, 8, 64, 1, 2, 1024, 0x0117, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x64, 0x64, 0x54, 0xa4, 0x00}, 4, 1, 32, 512, 2, 1, 1024, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x64, 0x44, 0x32, 0xa5, 0x00}, 4, 1, 32, 512, 2, 1, 1048, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x64, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32, 512, 2, 1, 1044, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x44, 0x34, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0xc4, 0x34, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x44, 0x34, 0xa4, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, {5, {0x2c, 0x84, 0x64, 0x3c, 0xa9, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x2c, 0xa4, 0x64, 0x32, 0xaa, 0x04}, 4, 1, 32, 1024, 2, 1, 2192, 0x05c7, 10, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x94, 0xd2, 0x04, 0x43}, 2, 1, 16, 256, 2, 2, 2048, 0x01d9, 1, 1, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3}, 2, 1, 16, 256, 2, 2, 1024, 0x01d9, 1, 2, 40, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd7, 0x94, 0x91, 0x60, 0x44}, 2, 1, 16, 256, 2, 2, 1046, 0x01d9, 1, 3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16, 256, 2, 2, 2090, 0x01d9, 1, 4, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x94, 0xeb, 0x74, 0x44}, 2, 1, 32, 256, 2, 2, 1066, 0x01d9, 1, 7, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd5, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16, 256, 2, 2, 530, 0x01d9, 1, 3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd7, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16, 256, 2, 2, 1024, 0x0119, 1, 0, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 1060, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd7, 0x14, 0x9e, 0x34, 0x4a}, 2, 1, 16, 256, 2, 2, 1056, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x94, 0xa7, 0x42, 0x48}, 2, 1, 32, 256, 2, 2, 1060, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xde, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 1056, 0x01d9, 2, 6, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0x3a, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 2092, 0x01d9, 2, 5, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0xd5, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16, 256, 2, 1, 1024, 0x0111, 1, 0, 24, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xad, 0x3a, 0x14, 0x03, 0x08, 0x50}, 2, 1, 32, 388, 2, 2, 1362, 0x01d9, 9, 8, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 7, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x84}, 7, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 7, 1, 16, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x68, 0x24, 0x4a, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x68, 0x04, 0x46, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x89, 0x64, 0x64, 0x3c, 0xa1, 0x00}, 7, 1, 32, 512, 2, 1, 1024, 0x01c7, 4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {5, {0x89, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 7, 1, 32, 512, 2, 2, 1024, 0x01c7, 4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x89, 0x88, 0x24, 0x3b, 0xa9, 0x00}, 7, 1, 16, 192, 2, 2, 2048, 0x0117, 12, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32, 256, 2, 1, 2092, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0x3a, 0x85, 0x93, 0x76, 0x57}, 1, 2, 32, 256, 2, 1, 2092, 0x05e1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd5, 0x84, 0x32, 0x72, 0x56}, 1, 1, 16, 128, 2, 1, 2056, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56}, 1, 1, 16, 128, 2, 2, 2058, 0x05d1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x94, 0x82, 0x76, 0x56}, 1, 1, 16, 256, 2, 2, 2062, 0x05d1, 1, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x50}, 1, 1, 32, 256, 2, 2, 1066, 0x05d9, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0x3a, 0x95, 0x93, 0x7a, 0x50}, 1, 2, 32, 256, 2, 2, 1066, 0x05d9, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x55}, 1, 1, 16, 128, 2, 2, 2050, 0x0191, 2, 0, 24, 32, 1, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x57}, 1, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 33, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x50}, 1, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32, 256, 2, 2, 1074, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0x3a, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32, 256, 2, 2, 2106, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x51}, 1, 1, 32, 256, 2, 1, 1056, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0xd1}, 1, 1, 32, 256, 2, 2, 1074, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x57}, 8, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 66, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0x56}, 8, 1, 16, 256, 2, 2, 2082, 0x01d9, 1, 65, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x50}, 8, 1, 32, 256, 2, 2, 1066, 0x05d9, 2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x50}, 8, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0xd7}, 8, 1, 16, 256, 2, 2, 2090, 0x04d9, 1, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32, 256, 2, 1, 2092, 0x05c1, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0x3a, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32, 256, 2, 2, 2106, 0x01d9, 2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32, 256, 2, 2, 1074, 0x01d9, 2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0x3a, 0xa4, 0x93, 0x7a, 0x50}, 8, 1, 32, 256, 2, 2, 2138, 0x05d9, 2, 0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x94, 0x82, 0x76, 0x56}, 8, 1, 16, 256, 2, 2, 2062, 0x01d9, 1, 0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0xd7}, 8, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xd7, 0x94, 0x7e, 0x64, 0x44}, 0, 1, 16, 128, 2, 2, 2048, 0x01d9, 2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xde, 0xd5, 0x7e, 0x68, 0x44}, 0, 2, 16, 128, 2, 2, 2048, 0x01f9, 2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xd7, 0x94, 0x7a, 0x54, 0x43}, 0, 1, 16, 128, 2, 2, 2076, 0x0199, 2, 0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xde, 0xd5, 0x7a, 0x58, 0x43}, 0, 2, 16, 128, 2, 2, 2076, 0x01b9, 2, 0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xd5, 0x94, 0x76, 0x54, 0x43}, 0, 1, 16, 128, 2, 2, 1038, 0x0119, 2, 0, 24, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xd7, 0x14, 0x76, 0x54, 0xc2}, 0, 1, 16, 128, 2, 2, 2076, 0x0491, 2, 0, 24, 40, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, {6, {0xec, 0xde, 0x94, 0xc3, 0xa4, 0xca}, 0, 1, 32, 792, 2, 1, 688, 0x04c1, 11, 50, 40, 32, 3, 1, 1, 0, 1, {0, 0, 0, 0, 0}}, }; #if !IS_ENABLED(CONFIG_SANDBOX) static int rkmtd_write_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf) { struct mtd_info *mtd = plat->mtd; struct mtd_oob_ops ops; loff_t addr; int ret; off &= ~(mtd->writesize - 1); addr = (loff_t)off; memset(&ops, 0, sizeof(ops)); ops.datbuf = datbuf; ops.oobbuf = oobbuf; ops.len = mtd->writesize; ops.ooblen = mtd->oobsize; ops.mode = MTD_OPS_PLACE_OOB; ret = mtd_write_oob(mtd, addr, &ops); if (ret < 0) { debug("Error (%d) writing page %08lx\n", ret, off); return 1; } return 0; } static int rkmtd_read_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf) { struct mtd_info *mtd = plat->mtd; struct mtd_oob_ops ops; loff_t addr; int ret; off &= ~(mtd->writesize - 1); addr = (loff_t)off; memset(&ops, 0, sizeof(ops)); ops.datbuf = datbuf; ops.oobbuf = oobbuf; ops.len = mtd->writesize; ops.ooblen = mtd->oobsize; ops.mode = MTD_OPS_PLACE_OOB; ret = mtd_read_oob(mtd, addr, &ops); if (ret < 0) { debug("Error (%d) reading page %08lx\n", ret, off); return 1; } return 0; } static int rkmtd_erase(struct rkmtd_dev *plat, ulong off) { struct mtd_info *mtd = plat->mtd; struct erase_info info; loff_t addr; int ret; off &= ~(mtd->writesize - 1); addr = (loff_t)off; memset(&info, 0, sizeof(info)); info.mtd = mtd; info.addr = addr; info.len = mtd->erasesize; info.scrub = 1; ret = mtd_erase(mtd, &info); if (ret) { debug("Error (%d) erasing page %08lx\n", ret, off); return 1; } return 0; } #endif void rkmtd_scan_block(struct rkmtd_dev *plat) { plat->blk_counter = 0; #if !IS_ENABLED(CONFIG_SANDBOX) u32 blk; for (blk = 0; blk < plat->boot_blks; blk++) { rkmtd_read_oob(plat, blk * plat->mtd->erasesize, plat->datbuf, plat->oobbuf); if (*(u32 *)plat->datbuf == RK_TAG) { struct sector0 *sec0 = (struct sector0 *)plat->datbuf; rkmtd_rc4(plat->datbuf, 512); plat->idblock[plat->blk_counter].blk = blk; plat->idblock[plat->blk_counter].offset = sec0->boot_code1_offset; plat->idblock[plat->blk_counter].boot_size = sec0->flash_boot_size; debug("\nblk : %d\n", plat->idblock[plat->blk_counter].blk); debug("offset : %d\n", plat->idblock[plat->blk_counter].offset); debug("boot_size : %d\n", plat->idblock[plat->blk_counter].boot_size); plat->blk_counter += 1; if (plat->blk_counter >= ARRAY_SIZE(plat->idblock)) return; } } #endif } void rkmtd_read_block(struct rkmtd_dev *plat, u32 idx, u8 *buf) { #if !IS_ENABLED(CONFIG_SANDBOX) ulong off = plat->idblock[idx].blk * plat->mtd->erasesize; struct nand_chip *chip = mtd_to_nand(plat->mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; int counter = 0; u32 spare0 = 0; u32 *p_spare; int sector; int page; rkmtd_read_oob(plat, off, plat->datbuf, plat->oobbuf); memcpy(buf, plat->datbuf, BLK_SIZE); while (counter < plat->idblock[idx].boot_size) { if (spare0) page = (plat->idblock[idx].offset + spare0) / 4; else page = (plat->idblock[idx].offset + counter) / 4; rkmtd_read_oob(plat, off + page * plat->mtd->writesize, plat->datbuf, plat->oobbuf); sector = plat->idblock[idx].offset + counter; memcpy(&buf[(sector / 4) * BLK_SIZE], plat->datbuf, BLK_SIZE); p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE]; spare0 = *p_spare; if (spare0 == -1) break; counter += 4; } #endif } void rkmtd_write_block(struct rkmtd_dev *plat, u32 idx, u8 *buf) { #if !IS_ENABLED(CONFIG_SANDBOX) ulong off = plat->idblock[idx].blk * plat->mtd->erasesize; struct nand_chip *chip = mtd_to_nand(plat->mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; int counter = 0; u32 *p_spare; int sector; int page; int j, w, r; rkmtd_erase(plat, off); memset(plat->datbuf, 0xff, plat->mtd->writesize); memcpy(plat->datbuf, buf, BLK_SIZE); memset(plat->oobbuf, 0xff, plat->mtd->oobsize); rkmtd_write_oob(plat, off, plat->datbuf, plat->oobbuf); while (counter < plat->idblock[idx].boot_size) { sector = plat->idblock[idx].offset + counter; memset(plat->datbuf, 0xff, plat->mtd->writesize); memcpy(plat->datbuf, &buf[(sector / 4) * BLK_SIZE], BLK_SIZE); memset(plat->oobbuf, 0xff, plat->mtd->oobsize); p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE]; *p_spare = (plat->page_table[sector / 4 + 1] - 1) * 4; page = plat->page_table[sector / 4]; rkmtd_write_oob(plat, off + page * plat->mtd->writesize, plat->datbuf, plat->oobbuf); counter += 4; } memset(plat->check, 0, BUF_SIZE); rkmtd_read_block(plat, idx, plat->check); for (j = 0; j < BLK_SIZE; j++) { w = *(buf + j); r = *(plat->check + j); if (r != w) goto dumpblock; } for (j = 0; j < (plat->idblock[idx].boot_size * 512); j++) { w = *(buf + plat->idblock[idx].offset * 512 + j); r = *(plat->check + plat->idblock[idx].offset * 512 + j); if (r != w) goto dumpblock; } debug("write OK\n"); return; dumpblock: debug("write and check error:%x r=%x w=%x\n", j, r, w); plat->idblock[idx].offset = 0; plat->idblock[idx].boot_size = 0; memset(plat->datbuf, 0xff, plat->mtd->writesize); memset(plat->datbuf, 0, BLK_SIZE); memset(plat->oobbuf, 0xff, plat->mtd->oobsize); rkmtd_write_oob(plat, off, plat->datbuf, plat->oobbuf); #endif } ulong rkmtd_bread(struct udevice *udev, lbaint_t start, lbaint_t blkcnt, void *dst) { struct blk_desc *block_dev = dev_get_uclass_plat(udev); struct udevice *parent_dev = dev_get_parent(udev); struct rkmtd_dev *plat = dev_get_plat(parent_dev); char *buf = dst; int i; if (blkcnt == 0) return 0; if (start > (block_dev->lba - 1) || (start + blkcnt) > block_dev->lba) return 0; memset(dst, 0, blkcnt * block_dev->blksz); for (i = start; i < (start + blkcnt); i++) { if (i == 0) { debug("mbr : %d\n", i); memcpy(&buf[(i - start) * block_dev->blksz], plat->mbr, sizeof(legacy_mbr)); } else if (i == 1) { debug("gpt_h : %d\n", i); memcpy(&buf[(i - start) * block_dev->blksz], plat->gpt_h, sizeof(gpt_header)); } else if (i == (block_dev->lba - 1)) { debug("gpt_h2 : %d\n", i); memcpy(&buf[(i - start) * block_dev->blksz], plat->gpt_h2, sizeof(gpt_header)); } else if (i == 2 || i == (block_dev->lba - 33)) { debug("gpt_e : %d\n", i); memcpy(&buf[(i - start) * block_dev->blksz], plat->gpt_e, sizeof(gpt_entry)); } else if (i >= 64 && i < (block_dev->lba - 33)) { debug("rd : %d\n", i); memcpy(&buf[(i - start) * block_dev->blksz], &plat->idb[(i - 64) * block_dev->blksz], block_dev->blksz); } } return blkcnt; } ulong rkmtd_bwrite(struct udevice *udev, lbaint_t start, lbaint_t blkcnt, const void *src) { struct blk_desc *block_dev = dev_get_uclass_plat(udev); struct udevice *parent_dev = dev_get_parent(udev); struct rkmtd_dev *plat = dev_get_plat(parent_dev); struct sector0 *sec0; int i, j; if (blkcnt == 0) return 0; if (start > (block_dev->lba - 1) || (start + blkcnt) > block_dev->lba) return 0; for (i = start; i < (start + blkcnt); i++) { debug("wr : %d\n", i); if (i >= 64 && i < (block_dev->lba - 33)) { if (i == 64) { debug("first block\n"); plat->idb_need_write_back = 1; memset(plat->idb, 0, BUF_SIZE); } if (plat->idb_need_write_back) { char *buf = (char *)src; memcpy(&plat->idb[(i - 64) * block_dev->blksz], &buf[(i - start) * block_dev->blksz], block_dev->blksz); if (i == 64) { memcpy(plat->check, plat->idb, 512); if (*(u32 *)plat->check == RK_TAG) { rkmtd_rc4(plat->check, 512); sec0 = (struct sector0 *)plat->check; plat->offset = sec0->boot_code1_offset; plat->boot_size = sec0->flash_boot_size; if (plat->offset + plat->boot_size > 512) { debug("max size limit\n"); plat->idb_need_write_back = 0; } } else { debug("no IDB block found\n"); plat->idb_need_write_back = 0; } } if (i == (64 + plat->offset + plat->boot_size - 1)) { debug("last block\n"); plat->idb_need_write_back = 0; if (!plat->blk_counter) { plat->idblock[0].blk = 2; plat->idblock[1].blk = 3; plat->idblock[2].blk = 4; plat->idblock[3].blk = 5; plat->idblock[4].blk = 6; plat->blk_counter = 5; } for (j = 0; j < plat->blk_counter; j++) { if (plat->idblock[j].blk < plat->boot_blks) { plat->idblock[j].offset = plat->offset; plat->idblock[j].boot_size = plat->boot_size; rkmtd_write_block(plat, j, plat->idb); } } rkmtd_scan_block(plat); if (!IS_ENABLED(CONFIG_SANDBOX)) memset(plat->idb, 0, BUF_SIZE); if (plat->blk_counter) rkmtd_read_block(plat, 0, plat->idb); } } } else if (plat->idb_need_write_back) { plat->idb_need_write_back = 0; memset(plat->idb, 0, BUF_SIZE); if (plat->blk_counter) rkmtd_read_block(plat, 0, plat->idb); } } return blkcnt; } static const struct blk_ops rkmtd_blk_ops = { .read = rkmtd_bread, .write = rkmtd_bwrite, }; U_BOOT_DRIVER(rkmtd_blk) = { .name = "rkmtd_blk", .id = UCLASS_BLK, .ops = &rkmtd_blk_ops, }; void rkmtd_build_page_table(struct rkmtd_dev *plat) { u32 counter; u32 counter2; switch (plat->lsb_mode) { case 0: counter = 0; do { u16 val = counter; plat->page_table[counter++] = val; } while (counter != 512); break; case 1: counter = 0; do { u16 val = counter; if (counter > 3) { u16 offset; if (counter & 1) offset = 3; else offset = 2; val = 2 * counter - offset; } plat->page_table[counter++] = val; } while (counter != 512); break; case 2: counter = 0; do { u16 val = counter; if (counter > 1) val = 2 * counter - 1; plat->page_table[counter++] = val; } while (counter != 512); break; case 3: counter = 0; do { u16 val = counter; if (counter > 5) { u16 offset; if (counter & 1) offset = 5; else offset = 4; val = 2 * counter - offset; } plat->page_table[counter++] = val; } while (counter != 512); break; case 4: counter = 8; plat->page_table[0] = 0; plat->page_table[1] = 1; plat->page_table[2] = 2; plat->page_table[3] = 3; plat->page_table[4] = 4; plat->page_table[5] = 5; plat->page_table[6] = 7; plat->page_table[7] = 8; do { u32 offset; u32 val; if (counter & 1) offset = 7; else offset = 6; val = 2 * counter - offset; plat->page_table[counter++] = val; } while (counter != 512); break; case 5: counter = 0; counter2 = 16; do { u16 val = counter; plat->page_table[counter++] = val; } while (counter != 16); do { plat->page_table[counter++] = counter2; counter2 = counter2 + 2; } while (counter != 512); break; case 6: counter = 0; counter2 = 0; do { u16 val = counter; if (counter > 5) { u16 offset; if (counter & 1) offset = 12; else offset = 10; val = counter2 - offset; } plat->page_table[counter++] = val; counter2 = counter2 + 3; } while (counter != 512); break; case 9: counter = 3; counter2 = 3; plat->page_table[0] = 0; plat->page_table[1] = 1; plat->page_table[2] = 2; do { plat->page_table[counter++] = counter2; counter2 = counter2 + 2; } while (counter != 512); break; case 10: counter = 0; counter2 = 63; do { u16 val = counter; plat->page_table[counter++] = val; } while (counter != 63); do { plat->page_table[counter++] = counter2; counter2 = counter2 + 2; } while (counter != 512); break; case 11: counter = 0; do { u16 val = counter; plat->page_table[counter++] = val; } while (counter != 8); do { u32 offset; u32 val; if (counter & 1) offset = 7; else offset = 6; val = 2 * counter - offset; plat->page_table[counter++] = val; } while (counter != 512); break; case 12: counter = 4; plat->page_table[0] = 0; plat->page_table[1] = 1; plat->page_table[2] = 2; plat->page_table[3] = 3; do { u32 val = counter - 1 + (counter >> 1); plat->page_table[counter++] = val; } while (counter != 512); break; } } static inline u32 efi_crc32(const void *buf, u32 len) { return crc32(0, buf, len); } int rkmtd_init_plat(struct udevice *dev) { static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID; struct rkmtd_dev *plat = dev_get_plat(dev); size_t efiname_len, dosname_len; uchar name[] = "loader1"; u32 calc_crc32; int k; gen_rand_uuid_str(plat->uuid_disk_str, UUID_STR_FORMAT_GUID); gen_rand_uuid_str(plat->uuid_part_str, UUID_STR_FORMAT_GUID); debug("uuid_part_str : %s\n", plat->uuid_part_str); debug("uuid_disk_str : %s\n", plat->uuid_disk_str); plat->idb = devm_kzalloc(plat->dev, BUF_SIZE, GFP_KERNEL); if (!plat->idb) return -ENOMEM; plat->check = devm_kzalloc(plat->dev, BUF_SIZE, GFP_KERNEL); if (!plat->check) return -ENOMEM; plat->mbr = devm_kzalloc(plat->dev, sizeof(legacy_mbr), GFP_KERNEL); if (!plat->mbr) return -ENOMEM; plat->gpt_e = devm_kzalloc(plat->dev, sizeof(gpt_entry), GFP_KERNEL); if (!plat->gpt_e) return -ENOMEM; plat->gpt_h = devm_kzalloc(plat->dev, sizeof(gpt_header), GFP_KERNEL); if (!plat->gpt_h) return -ENOMEM; plat->gpt_h2 = devm_kzalloc(plat->dev, sizeof(gpt_header), GFP_KERNEL); if (!plat->gpt_h2) return -ENOMEM; /* Init mbr */ plat->mbr->signature = MSDOS_MBR_SIGNATURE; plat->mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT; plat->mbr->partition_record[0].start_sect = 1; plat->mbr->partition_record[0].nr_sects = LBA - 1; /* Init gpt_e */ plat->gpt_e->starting_lba = cpu_to_le64(64); plat->gpt_e->ending_lba = cpu_to_le64(LBA - 34); debug("starting_lba : %llu\n", le64_to_cpu(plat->gpt_e->starting_lba)); debug("ending_lba : %llu\n", le64_to_cpu(plat->gpt_e->ending_lba)); memcpy(plat->gpt_e->partition_type_guid.b, &partition_basic_data_guid, 16); uuid_str_to_bin(plat->uuid_part_str, plat->gpt_e->unique_partition_guid.b, UUID_STR_FORMAT_GUID); efiname_len = sizeof(plat->gpt_e->partition_name) / sizeof(efi_char16_t); dosname_len = sizeof(name); for (k = 0; k < min(dosname_len, efiname_len); k++) plat->gpt_e->partition_name[k] = (efi_char16_t)(name[k]); /* Init gpt_h */ plat->gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE_UBOOT); plat->gpt_h->revision = cpu_to_le32(GPT_HEADER_REVISION_V1); plat->gpt_h->header_size = cpu_to_le32(sizeof(gpt_header)); plat->gpt_h->first_usable_lba = cpu_to_le64(64); plat->gpt_h->last_usable_lba = cpu_to_le64(LBA - 34); plat->gpt_h->num_partition_entries = cpu_to_le32(1); plat->gpt_h->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry)); uuid_str_to_bin(plat->uuid_disk_str, plat->gpt_h->disk_guid.b, UUID_STR_FORMAT_GUID); plat->gpt_h->partition_entry_array_crc32 = 0; calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_e, le32_to_cpu(plat->gpt_h->num_partition_entries) * le32_to_cpu(plat->gpt_h->sizeof_partition_entry)); plat->gpt_h->partition_entry_array_crc32 = cpu_to_le32(calc_crc32); debug("partition crc32 : 0x%08x\n", calc_crc32); plat->gpt_h->my_lba = cpu_to_le64(1); plat->gpt_h->partition_entry_lba = cpu_to_le64(2); plat->gpt_h->alternate_lba = cpu_to_le64(LBA - 1); plat->gpt_h->header_crc32 = 0; calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h, le32_to_cpu(plat->gpt_h->header_size)); plat->gpt_h->header_crc32 = cpu_to_le32(calc_crc32); debug("header h1 crc32 : 0x%08x\n", calc_crc32); /* Init gpt_h2 */ memcpy(plat->gpt_h2, plat->gpt_h, sizeof(gpt_header)); plat->gpt_h2->my_lba = cpu_to_le64(LBA - 1); plat->gpt_h2->partition_entry_lba = cpu_to_le64(le64_to_cpu(plat->gpt_h2->last_usable_lba) + 1); plat->gpt_h2->alternate_lba = cpu_to_le64(1); plat->gpt_h2->header_crc32 = 0; calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h2, le32_to_cpu(plat->gpt_h2->header_size)); plat->gpt_h2->header_crc32 = cpu_to_le32(calc_crc32); debug("header h2 crc32 : 0x%08x\n", calc_crc32); part_init(plat->desc); return 0; } static void rkmtd_blk_kmalloc_release(struct udevice *dev, void *res) { /* noop */ } static int rkmtd_bind(struct udevice *dev) { struct rkmtd_dev *plat = dev_get_plat(dev); char dev_name[30], *str; struct blk_desc *desc; struct udevice *bdev; int ret; snprintf(dev_name, sizeof(dev_name), "%s.%s", dev->name, "blk"); str = devres_alloc(rkmtd_blk_kmalloc_release, strlen(dev_name) + 1, GFP_KERNEL); if (unlikely(!str)) return -ENOMEM; strcpy(str, dev_name); ret = blk_create_device(dev, "rkmtd_blk", str, UCLASS_RKMTD, -1, 512, LBA, &bdev); if (ret) { free(str); return log_msg_ret("blk", ret); } devres_add(dev, str); desc = dev_get_uclass_plat(bdev); sprintf(desc->vendor, "0x%.4x", 0x2207); memcpy(desc->product, "RKMTD", sizeof("RKMTD")); memcpy(desc->revision, "V1.00", sizeof("V1.00")); plat->desc = desc; return 0; } static int rkmtd_attach_mtd(struct udevice *dev) { struct rkmtd_dev *plat = dev_get_plat(dev); struct mtd_info *mtd; struct udevice *blk; int ret; plat->dev = dev; /* Sanity check that rkmtd_bind() has been used */ ret = blk_find_from_parent(dev, &blk); if (ret) return ret; #if IS_ENABLED(CONFIG_SANDBOX) plat->mtd = devm_kzalloc(dev, sizeof(struct mtd_info), GFP_KERNEL); if (!plat->mtd) return -ENOMEM; mtd = plat->mtd; mtd->erasesize = 2 ^ 3 * BLK_SIZE; mtd->writesize = BLK_SIZE; mtd->oobsize = BLK_SIZE / STEP_SIZE * NFC_SYS_DATA_SIZE; plat->boot_blks = 0; plat->lsb_mode = 0; #else struct nand_chip *chip; u8 id[6]; int i, j; u32 tmp; mtd = get_nand_dev_by_index(0); if (!mtd) return -ENOSYS; chip = mtd_to_nand(mtd); ret = ofnode_read_u32(chip->flash_node, "rockchip,boot-blks", &tmp); plat->boot_blks = ret ? 0 : tmp; plat->mtd = mtd; if (chip->select_chip) chip->select_chip(mtd, 0); nand_readid_op(chip, 0, id, 6); if (chip->select_chip) chip->select_chip(mtd, -1); for (i = 0; i < ARRAY_SIZE(nand_para_tbl); i++) { plat->info = (struct nand_para_info *)&nand_para_tbl[i]; for (j = 0; j < plat->info->id_bytes; j++) { if (plat->info->nand_id[j] != id[j]) break; if (j == plat->info->id_bytes - 1) goto valid; } } debug("no nand_para_info found\n"); return -ENODEV; valid: plat->lsb_mode = plat->info->lsb_mode; debug("FLASH ID :"); for (j = 0; j < plat->info->id_bytes; j++) debug(" %x", id[j]); debug("\n"); #endif rkmtd_build_page_table(plat); plat->datbuf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL); if (!plat->datbuf) return -ENOMEM; plat->oobbuf = devm_kzalloc(dev, mtd->oobsize, GFP_KERNEL); if (!plat->oobbuf) return -ENOMEM; debug("erasesize %8d\n", mtd->erasesize); debug("writesize %8d\n", mtd->writesize); debug("oobsize %8d\n", mtd->oobsize); debug("boot_blks %8d\n", plat->boot_blks); debug("lsb_mode %8d\n", plat->lsb_mode); ret = rkmtd_init_plat(dev); if (ret) { debug("rkmtd_init_plat failed\n"); return -ENOENT; } rkmtd_scan_block(plat); memset(plat->idb, 0, BUF_SIZE); if (plat->blk_counter) rkmtd_read_block(plat, 0, plat->idb); return 0; } int rkmtd_detach_mtd(struct udevice *dev) { int ret; ret = device_remove(dev, DM_REMOVE_NORMAL); if (ret) return log_msg_ret("rem", ret); ret = device_chld_unbind(dev, NULL); if (ret) return log_msg_ret("unb", ret); return 0; } struct rkmtd_ops rkmtd_ops = { .attach_mtd = rkmtd_attach_mtd, .detach_mtd = rkmtd_detach_mtd, }; U_BOOT_DRIVER(rkmtd_drv) = { .name = "rkmtd_drv", .id = UCLASS_RKMTD, .ops = &rkmtd_ops, .bind = rkmtd_bind, .plat_auto = sizeof(struct rkmtd_dev), }; struct rkmtd_priv { struct udevice *cur_dev; }; void rkmtd_rc4(u8 *buf, u32 len) { u8 S[256], K[256], temp; u32 i, j, t, x; u8 key[16] = { 124, 78, 3, 4, 85, 5, 9, 7, 45, 44, 123, 56, 23, 13, 23, 17}; j = 0; for (i = 0; i < 256; i++) { S[i] = (u8)i; j &= 0x0f; K[i] = key[j]; j++; } j = 0; for (i = 0; i < 256; i++) { j = (j + S[i] + K[i]) % 256; temp = S[i]; S[i] = S[j]; S[j] = temp; } i = 0; j = 0; for (x = 0; x < len; x++) { i = (i + 1) % 256; j = (j + S[i]) % 256; temp = S[i]; S[i] = S[j]; S[j] = temp; t = (S[i] + (S[j] % 256)) % 256; buf[x] = buf[x] ^ S[t]; } } struct udevice *rkmtd_get_cur_dev(void) { struct uclass *uc = uclass_find(UCLASS_RKMTD); if (uc) { struct rkmtd_priv *priv = uclass_get_priv(uc); return priv->cur_dev; } return NULL; } void rkmtd_set_cur_dev(struct udevice *dev) { struct uclass *uc = uclass_find(UCLASS_RKMTD); if (uc) { struct rkmtd_priv *priv = uclass_get_priv(uc); priv->cur_dev = dev; } } struct udevice *rkmtd_find_by_label(const char *label) { struct udevice *dev; struct uclass *uc; uclass_id_foreach_dev(UCLASS_RKMTD, dev, uc) { struct rkmtd_dev *plat = dev_get_plat(dev); if (plat->label && !strcmp(label, plat->label)) return dev; } return NULL; } int rkmtd_attach(struct udevice *dev) { struct rkmtd_ops *ops = rkmtd_get_ops(dev); if (!ops->attach_mtd) return -ENOSYS; return ops->attach_mtd(dev); } int rkmtd_detach(struct udevice *dev) { struct rkmtd_ops *ops = rkmtd_get_ops(dev); if (!ops->detach_mtd) return -ENOSYS; if (dev == rkmtd_get_cur_dev()) rkmtd_set_cur_dev(NULL); return ops->detach_mtd(dev); } static void rkmtd_drv_kmalloc_release(struct udevice *dev, void *res) { /* noop */ } int rkmtd_create_device(const char *label, struct udevice **devp) { char dev_name[30], *str, *label_new; struct udevice *dev, *blk; struct rkmtd_dev *plat; int ret; /* unbind any existing device with this label */ dev = rkmtd_find_by_label(label); if (dev) { ret = rkmtd_detach(dev); if (ret) return log_msg_ret("det", ret); ret = device_unbind(dev); if (ret) return log_msg_ret("unb", ret); } snprintf(dev_name, sizeof(dev_name), "rkmtd-%s", label); str = devres_alloc(rkmtd_drv_kmalloc_release, strlen(dev_name) + 1, GFP_KERNEL); if (unlikely(!str)) return -ENOMEM; strcpy(str, dev_name); ret = device_bind_driver(dm_root(), "rkmtd_drv", str, &dev); if (ret) { free(str); return log_msg_ret("drv", ret); } devres_add(dev, str); if (!blk_find_from_parent(dev, &blk)) { struct blk_desc *desc = dev_get_uclass_plat(blk); desc->removable = true; } label_new = devm_kzalloc(dev, strlen(label) + 1, GFP_KERNEL); if (!label_new) return -ENOMEM; strcpy(label_new, label); plat = dev_get_plat(dev); plat->label = label_new; *devp = dev; return 0; } int rkmtd_create_attach_mtd(const char *label, struct udevice **devp) { struct udevice *dev; int ret; ret = rkmtd_create_device(label, &dev); if (ret) return log_msg_ret("cre", ret); ret = rkmtd_attach(dev); if (ret) { device_unbind(dev); return log_msg_ret("att", ret); } *devp = dev; return 0; } UCLASS_DRIVER(rkmtd) = { .name = "rkmtd", .id = UCLASS_RKMTD, .post_bind = dm_scan_fdt_dev, .priv_auto = sizeof(struct rkmtd_priv), };