diff options
Diffstat (limited to 'drivers/macintosh/smu.c')
-rw-r--r-- | drivers/macintosh/smu.c | 174 |
1 files changed, 168 insertions, 6 deletions
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 34f3c7e2d832..e8378274d710 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; |
@@ -588,6 +590,8 @@ static void smu_expose_childs(void *unused) | |||
588 | sprintf(name, "smu-i2c-%02x", *reg); | 590 | sprintf(name, "smu-i2c-%02x", *reg); |
589 | of_platform_device_create(np, name, &smu->of_dev->dev); | 591 | of_platform_device_create(np, name, &smu->of_dev->dev); |
590 | } | 592 | } |
593 | if (device_is_compatible(np, "smu-sensors")) | ||
594 | of_platform_device_create(np, "smu-sensors", &smu->of_dev->dev); | ||
591 | } | 595 | } |
592 | 596 | ||
593 | } | 597 | } |
@@ -845,6 +849,156 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd) | |||
845 | return 0; | 849 | return 0; |
846 | } | 850 | } |
847 | 851 | ||
852 | /* | ||
853 | * Handling of "partitions" | ||
854 | */ | ||
855 | |||
856 | static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) | ||
857 | { | ||
858 | DECLARE_COMPLETION(comp); | ||
859 | unsigned int chunk; | ||
860 | struct smu_cmd cmd; | ||
861 | int rc; | ||
862 | u8 params[8]; | ||
863 | |||
864 | /* We currently use a chunk size of 0xe. We could check the | ||
865 | * SMU firmware version and use bigger sizes though | ||
866 | */ | ||
867 | chunk = 0xe; | ||
868 | |||
869 | while (len) { | ||
870 | unsigned int clen = min(len, chunk); | ||
871 | |||
872 | cmd.cmd = SMU_CMD_MISC_ee_COMMAND; | ||
873 | cmd.data_len = 7; | ||
874 | cmd.data_buf = params; | ||
875 | cmd.reply_len = chunk; | ||
876 | cmd.reply_buf = dest; | ||
877 | cmd.done = smu_done_complete; | ||
878 | cmd.misc = ∁ | ||
879 | params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; | ||
880 | params[1] = 0x4; | ||
881 | *((u32 *)¶ms[2]) = addr; | ||
882 | params[6] = clen; | ||
883 | |||
884 | rc = smu_queue_cmd(&cmd); | ||
885 | if (rc) | ||
886 | return rc; | ||
887 | wait_for_completion(&comp); | ||
888 | if (cmd.status != 0) | ||
889 | return rc; | ||
890 | if (cmd.reply_len != clen) { | ||
891 | printk(KERN_DEBUG "SMU: short read in " | ||
892 | "smu_read_datablock, got: %d, want: %d\n", | ||
893 | cmd.reply_len, clen); | ||
894 | return -EIO; | ||
895 | } | ||
896 | len -= clen; | ||
897 | addr += clen; | ||
898 | dest += clen; | ||
899 | } | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static struct smu_sdbp_header *smu_create_sdb_partition(int id) | ||
904 | { | ||
905 | DECLARE_COMPLETION(comp); | ||
906 | struct smu_simple_cmd cmd; | ||
907 | unsigned int addr, len, tlen; | ||
908 | struct smu_sdbp_header *hdr; | ||
909 | struct property *prop; | ||
910 | |||
911 | /* First query the partition info */ | ||
912 | smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, | ||
913 | smu_done_complete, &comp, | ||
914 | SMU_CMD_PARTITION_LATEST, id); | ||
915 | wait_for_completion(&comp); | ||
916 | |||
917 | /* Partition doesn't exist (or other error) */ | ||
918 | if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) | ||
919 | return NULL; | ||
920 | |||
921 | /* Fetch address and length from reply */ | ||
922 | addr = *((u16 *)cmd.buffer); | ||
923 | len = cmd.buffer[3] << 2; | ||
924 | /* Calucluate total length to allocate, including the 17 bytes | ||
925 | * for "sdb-partition-XX" that we append at the end of the buffer | ||
926 | */ | ||
927 | tlen = sizeof(struct property) + len + 18; | ||
928 | |||
929 | prop = kcalloc(tlen, 1, GFP_KERNEL); | ||
930 | if (prop == NULL) | ||
931 | return NULL; | ||
932 | hdr = (struct smu_sdbp_header *)(prop + 1); | ||
933 | prop->name = ((char *)prop) + tlen - 18; | ||
934 | sprintf(prop->name, "sdb-partition-%02x", id); | ||
935 | prop->length = len; | ||
936 | prop->value = (unsigned char *)hdr; | ||
937 | prop->next = NULL; | ||
938 | |||
939 | /* Read the datablock */ | ||
940 | if (smu_read_datablock((u8 *)hdr, addr, len)) { | ||
941 | printk(KERN_DEBUG "SMU: datablock read failed while reading " | ||
942 | "partition %02x !\n", id); | ||
943 | goto failure; | ||
944 | } | ||
945 | |||
946 | /* Got it, check a few things and create the property */ | ||
947 | if (hdr->id != id) { | ||
948 | printk(KERN_DEBUG "SMU: Reading partition %02x and got " | ||
949 | "%02x !\n", id, hdr->id); | ||
950 | goto failure; | ||
951 | } | ||
952 | if (prom_add_property(smu->of_node, prop)) { | ||
953 | printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " | ||
954 | "property !\n", id); | ||
955 | goto failure; | ||
956 | } | ||
957 | |||
958 | return hdr; | ||
959 | failure: | ||
960 | kfree(prop); | ||
961 | return NULL; | ||
962 | } | ||
963 | |||
964 | /* Note: Only allowed to return error code in pointers (using ERR_PTR) | ||
965 | * when interruptible is 1 | ||
966 | */ | ||
967 | struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, | ||
968 | int interruptible) | ||
969 | { | ||
970 | char pname[32]; | ||
971 | struct smu_sdbp_header *part; | ||
972 | |||
973 | if (!smu) | ||
974 | return NULL; | ||
975 | |||
976 | sprintf(pname, "sdb-partition-%02x", id); | ||
977 | |||
978 | if (interruptible) { | ||
979 | int rc; | ||
980 | rc = down_interruptible(&smu_part_access); | ||
981 | if (rc) | ||
982 | return ERR_PTR(rc); | ||
983 | } else | ||
984 | down(&smu_part_access); | ||
985 | |||
986 | part = (struct smu_sdbp_header *)get_property(smu->of_node, | ||
987 | pname, size); | ||
988 | if (part == NULL) { | ||
989 | part = smu_create_sdb_partition(id); | ||
990 | if (part != NULL && size) | ||
991 | *size = part->len << 2; | ||
992 | } | ||
993 | up(&smu_part_access); | ||
994 | return part; | ||
995 | } | ||
996 | |||
997 | struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) | ||
998 | { | ||
999 | return __smu_get_sdb_partition(id, size, 0); | ||
1000 | } | ||
1001 | EXPORT_SYMBOL(smu_get_sdb_partition); | ||
848 | 1002 | ||
849 | 1003 | ||
850 | /* | 1004 | /* |
@@ -918,6 +1072,14 @@ static ssize_t smu_write(struct file *file, const char __user *buf, | |||
918 | else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { | 1072 | else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { |
919 | pp->mode = smu_file_events; | 1073 | pp->mode = smu_file_events; |
920 | return 0; | 1074 | return 0; |
1075 | } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { | ||
1076 | struct smu_sdbp_header *part; | ||
1077 | part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); | ||
1078 | if (part == NULL) | ||
1079 | return -EINVAL; | ||
1080 | else if (IS_ERR(part)) | ||
1081 | return PTR_ERR(part); | ||
1082 | return 0; | ||
921 | } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) | 1083 | } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) |
922 | return -EINVAL; | 1084 | return -EINVAL; |
923 | else if (pp->mode != smu_file_commands) | 1085 | else if (pp->mode != smu_file_commands) |