diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-06 22:29:02 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-11-07 19:17:40 -0500 |
commit | 183d020258dfd08178a05c6793dae10409db8abb (patch) | |
tree | 5b20bc62709c94bd63e17d800544140213eaf0f5 /drivers/macintosh | |
parent | 4350147a816b9c5b40fa59e4fa23f17490630b79 (diff) |
[PATCH] ppc64: SMU partition recovery
This patch adds the ability to the SMU driver to recover missing
calibration partitions from the SMU chip itself. It also adds some
dynamic mecanism to /proc/device-tree so that new properties are visible
to userland.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'drivers/macintosh')
-rw-r--r-- | drivers/macintosh/smu.c | 164 |
1 files changed, 156 insertions, 8 deletions
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index a931e508feb6..a83c4acf5710 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c | |||
@@ -47,13 +47,13 @@ | |||
47 | #include <asm/uaccess.h> | 47 | #include <asm/uaccess.h> |
48 | #include <asm/of_device.h> | 48 | #include <asm/of_device.h> |
49 | 49 | ||
50 | #define VERSION "0.6" | 50 | #define VERSION "0.7" |
51 | #define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." | 51 | #define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." |
52 | 52 | ||
53 | #undef DEBUG_SMU | 53 | #undef DEBUG_SMU |
54 | 54 | ||
55 | #ifdef DEBUG_SMU | 55 | #ifdef DEBUG_SMU |
56 | #define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) | 56 | #define DPRINTK(fmt, args...) do { udbg_printf(KERN_DEBUG fmt , ##args); } while (0) |
57 | #else | 57 | #else |
58 | #define DPRINTK(fmt, args...) do { } while (0) | 58 | #define DPRINTK(fmt, args...) do { } while (0) |
59 | #endif | 59 | #endif |
@@ -92,7 +92,7 @@ struct smu_device { | |||
92 | * for now, just hard code that | 92 | * for now, just hard code that |
93 | */ | 93 | */ |
94 | static struct smu_device *smu; | 94 | static struct smu_device *smu; |
95 | 95 | static DECLARE_MUTEX(smu_part_access); | |
96 | 96 | ||
97 | /* | 97 | /* |
98 | * SMU driver low level stuff | 98 | * SMU driver low level stuff |
@@ -113,9 +113,11 @@ static void smu_start_cmd(void) | |||
113 | 113 | ||
114 | DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, | 114 | DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, |
115 | cmd->data_len); | 115 | cmd->data_len); |
116 | DPRINTK("SMU: data buffer: %02x %02x %02x %02x ...\n", | 116 | DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
117 | ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], | 117 | ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], |
118 | ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3]); | 118 | ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3], |
119 | ((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5], | ||
120 | ((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]); | ||
119 | 121 | ||
120 | /* Fill the SMU command buffer */ | 122 | /* Fill the SMU command buffer */ |
121 | smu->cmd_buf->cmd = cmd->cmd; | 123 | smu->cmd_buf->cmd = cmd->cmd; |
@@ -440,7 +442,7 @@ int smu_present(void) | |||
440 | EXPORT_SYMBOL(smu_present); | 442 | EXPORT_SYMBOL(smu_present); |
441 | 443 | ||
442 | 444 | ||
443 | int smu_init (void) | 445 | int __init smu_init (void) |
444 | { | 446 | { |
445 | struct device_node *np; | 447 | struct device_node *np; |
446 | u32 *data; | 448 | u32 *data; |
@@ -845,16 +847,154 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd) | |||
845 | return 0; | 847 | return 0; |
846 | } | 848 | } |
847 | 849 | ||
848 | struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) | 850 | /* |
851 | * Handling of "partitions" | ||
852 | */ | ||
853 | |||
854 | static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) | ||
855 | { | ||
856 | DECLARE_COMPLETION(comp); | ||
857 | unsigned int chunk; | ||
858 | struct smu_cmd cmd; | ||
859 | int rc; | ||
860 | u8 params[8]; | ||
861 | |||
862 | /* We currently use a chunk size of 0xe. We could check the | ||
863 | * SMU firmware version and use bigger sizes though | ||
864 | */ | ||
865 | chunk = 0xe; | ||
866 | |||
867 | while (len) { | ||
868 | unsigned int clen = min(len, chunk); | ||
869 | |||
870 | cmd.cmd = SMU_CMD_MISC_ee_COMMAND; | ||
871 | cmd.data_len = 7; | ||
872 | cmd.data_buf = params; | ||
873 | cmd.reply_len = chunk; | ||
874 | cmd.reply_buf = dest; | ||
875 | cmd.done = smu_done_complete; | ||
876 | cmd.misc = ∁ | ||
877 | params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; | ||
878 | params[1] = 0x4; | ||
879 | *((u32 *)¶ms[2]) = addr; | ||
880 | params[6] = clen; | ||
881 | |||
882 | rc = smu_queue_cmd(&cmd); | ||
883 | if (rc) | ||
884 | return rc; | ||
885 | wait_for_completion(&comp); | ||
886 | if (cmd.status != 0) | ||
887 | return rc; | ||
888 | if (cmd.reply_len != clen) { | ||
889 | printk(KERN_DEBUG "SMU: short read in " | ||
890 | "smu_read_datablock, got: %d, want: %d\n", | ||
891 | cmd.reply_len, clen); | ||
892 | return -EIO; | ||
893 | } | ||
894 | len -= clen; | ||
895 | addr += clen; | ||
896 | dest += clen; | ||
897 | } | ||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | static struct smu_sdbp_header *smu_create_sdb_partition(int id) | ||
902 | { | ||
903 | DECLARE_COMPLETION(comp); | ||
904 | struct smu_simple_cmd cmd; | ||
905 | unsigned int addr, len, tlen; | ||
906 | struct smu_sdbp_header *hdr; | ||
907 | struct property *prop; | ||
908 | |||
909 | /* First query the partition info */ | ||
910 | smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, | ||
911 | smu_done_complete, &comp, | ||
912 | SMU_CMD_PARTITION_LATEST, id); | ||
913 | wait_for_completion(&comp); | ||
914 | |||
915 | /* Partition doesn't exist (or other error) */ | ||
916 | if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) | ||
917 | return NULL; | ||
918 | |||
919 | /* Fetch address and length from reply */ | ||
920 | addr = *((u16 *)cmd.buffer); | ||
921 | len = cmd.buffer[3] << 2; | ||
922 | /* Calucluate total length to allocate, including the 17 bytes | ||
923 | * for "sdb-partition-XX" that we append at the end of the buffer | ||
924 | */ | ||
925 | tlen = sizeof(struct property) + len + 18; | ||
926 | |||
927 | prop = kcalloc(tlen, 1, GFP_KERNEL); | ||
928 | if (prop == NULL) | ||
929 | return NULL; | ||
930 | hdr = (struct smu_sdbp_header *)(prop + 1); | ||
931 | prop->name = ((char *)prop) + tlen - 18; | ||
932 | sprintf(prop->name, "sdb-partition-%02x", id); | ||
933 | prop->length = len; | ||
934 | prop->value = (unsigned char *)hdr; | ||
935 | prop->next = NULL; | ||
936 | |||
937 | /* Read the datablock */ | ||
938 | if (smu_read_datablock((u8 *)hdr, addr, len)) { | ||
939 | printk(KERN_DEBUG "SMU: datablock read failed while reading " | ||
940 | "partition %02x !\n", id); | ||
941 | goto failure; | ||
942 | } | ||
943 | |||
944 | /* Got it, check a few things and create the property */ | ||
945 | if (hdr->id != id) { | ||
946 | printk(KERN_DEBUG "SMU: Reading partition %02x and got " | ||
947 | "%02x !\n", id, hdr->id); | ||
948 | goto failure; | ||
949 | } | ||
950 | if (prom_add_property(smu->of_node, prop)) { | ||
951 | printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " | ||
952 | "property !\n", id); | ||
953 | goto failure; | ||
954 | } | ||
955 | |||
956 | return hdr; | ||
957 | failure: | ||
958 | kfree(prop); | ||
959 | return NULL; | ||
960 | } | ||
961 | |||
962 | /* Note: Only allowed to return error code in pointers (using ERR_PTR) | ||
963 | * when interruptible is 1 | ||
964 | */ | ||
965 | struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, | ||
966 | int interruptible) | ||
849 | { | 967 | { |
850 | char pname[32]; | 968 | char pname[32]; |
969 | struct smu_sdbp_header *part; | ||
851 | 970 | ||
852 | if (!smu) | 971 | if (!smu) |
853 | return NULL; | 972 | return NULL; |
854 | 973 | ||
855 | sprintf(pname, "sdb-partition-%02x", id); | 974 | sprintf(pname, "sdb-partition-%02x", id); |
856 | return (struct smu_sdbp_header *)get_property(smu->of_node, | 975 | |
976 | if (interruptible) { | ||
977 | int rc; | ||
978 | rc = down_interruptible(&smu_part_access); | ||
979 | if (rc) | ||
980 | return ERR_PTR(rc); | ||
981 | } else | ||
982 | down(&smu_part_access); | ||
983 | |||
984 | part = (struct smu_sdbp_header *)get_property(smu->of_node, | ||
857 | pname, size); | 985 | pname, size); |
986 | if (part == NULL) { | ||
987 | part = smu_create_sdb_partition(id); | ||
988 | if (part != NULL && size) | ||
989 | *size = part->len << 2; | ||
990 | } | ||
991 | up(&smu_part_access); | ||
992 | return part; | ||
993 | } | ||
994 | |||
995 | struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) | ||
996 | { | ||
997 | return __smu_get_sdb_partition(id, size, 0); | ||
858 | } | 998 | } |
859 | EXPORT_SYMBOL(smu_get_sdb_partition); | 999 | EXPORT_SYMBOL(smu_get_sdb_partition); |
860 | 1000 | ||
@@ -930,6 +1070,14 @@ static ssize_t smu_write(struct file *file, const char __user *buf, | |||
930 | else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { | 1070 | else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { |
931 | pp->mode = smu_file_events; | 1071 | pp->mode = smu_file_events; |
932 | return 0; | 1072 | return 0; |
1073 | } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { | ||
1074 | struct smu_sdbp_header *part; | ||
1075 | part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); | ||
1076 | if (part == NULL) | ||
1077 | return -EINVAL; | ||
1078 | else if (IS_ERR(part)) | ||
1079 | return PTR_ERR(part); | ||
1080 | return 0; | ||
933 | } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) | 1081 | } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) |
934 | return -EINVAL; | 1082 | return -EINVAL; |
935 | else if (pp->mode != smu_file_commands) | 1083 | else if (pp->mode != smu_file_commands) |