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) |
