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 | |
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>
-rw-r--r-- | arch/powerpc/kernel/prom.c | 21 | ||||
-rw-r--r-- | arch/ppc/syslib/prom.c | 4 | ||||
-rw-r--r-- | arch/ppc64/kernel/prom.c | 24 | ||||
-rw-r--r-- | drivers/macintosh/smu.c | 164 | ||||
-rw-r--r-- | fs/proc/proc_devtree.c | 57 | ||||
-rw-r--r-- | include/asm-powerpc/prom.h | 2 | ||||
-rw-r--r-- | include/asm-powerpc/smu.h | 153 | ||||
-rw-r--r-- | include/asm-ppc/prom.h | 2 | ||||
-rw-r--r-- | include/asm-ppc64/prom.h | 2 | ||||
-rw-r--r-- | include/linux/proc_fs.h | 9 |
10 files changed, 381 insertions, 57 deletions
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 3675ef4bac90..f645adb57534 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c | |||
@@ -1974,14 +1974,29 @@ EXPORT_SYMBOL(get_property); | |||
1974 | /* | 1974 | /* |
1975 | * Add a property to a node | 1975 | * Add a property to a node |
1976 | */ | 1976 | */ |
1977 | void prom_add_property(struct device_node* np, struct property* prop) | 1977 | int prom_add_property(struct device_node* np, struct property* prop) |
1978 | { | 1978 | { |
1979 | struct property **next = &np->properties; | 1979 | struct property **next; |
1980 | 1980 | ||
1981 | prop->next = NULL; | 1981 | prop->next = NULL; |
1982 | while (*next) | 1982 | write_lock(&devtree_lock); |
1983 | next = &np->properties; | ||
1984 | while (*next) { | ||
1985 | if (strcmp(prop->name, (*next)->name) == 0) { | ||
1986 | /* duplicate ! don't insert it */ | ||
1987 | write_unlock(&devtree_lock); | ||
1988 | return -1; | ||
1989 | } | ||
1983 | next = &(*next)->next; | 1990 | next = &(*next)->next; |
1991 | } | ||
1984 | *next = prop; | 1992 | *next = prop; |
1993 | write_unlock(&devtree_lock); | ||
1994 | |||
1995 | /* try to add to proc as well if it was initialized */ | ||
1996 | if (np->pde) | ||
1997 | proc_device_tree_add_prop(np->pde, prop); | ||
1998 | |||
1999 | return 0; | ||
1985 | } | 2000 | } |
1986 | 2001 | ||
1987 | /* I quickly hacked that one, check against spec ! */ | 2002 | /* I quickly hacked that one, check against spec ! */ |
diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c index 1b9aa0d6a924..03b1fc9b9501 100644 --- a/arch/ppc/syslib/prom.c +++ b/arch/ppc/syslib/prom.c | |||
@@ -1165,7 +1165,7 @@ get_property(struct device_node *np, const char *name, int *lenp) | |||
1165 | /* | 1165 | /* |
1166 | * Add a property to a node | 1166 | * Add a property to a node |
1167 | */ | 1167 | */ |
1168 | void | 1168 | int |
1169 | prom_add_property(struct device_node* np, struct property* prop) | 1169 | prom_add_property(struct device_node* np, struct property* prop) |
1170 | { | 1170 | { |
1171 | struct property **next = &np->properties; | 1171 | struct property **next = &np->properties; |
@@ -1174,6 +1174,8 @@ prom_add_property(struct device_node* np, struct property* prop) | |||
1174 | while (*next) | 1174 | while (*next) |
1175 | next = &(*next)->next; | 1175 | next = &(*next)->next; |
1176 | *next = prop; | 1176 | *next = prop; |
1177 | |||
1178 | return 0; | ||
1177 | } | 1179 | } |
1178 | 1180 | ||
1179 | /* I quickly hacked that one, check against spec ! */ | 1181 | /* I quickly hacked that one, check against spec ! */ |
diff --git a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c index 0e8961dea3bc..3402fbee62c7 100644 --- a/arch/ppc64/kernel/prom.c +++ b/arch/ppc64/kernel/prom.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/initrd.h> | 31 | #include <linux/initrd.h> |
32 | #include <linux/bitops.h> | 32 | #include <linux/bitops.h> |
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | #include <linux/module.h> | ||
34 | 35 | ||
35 | #include <asm/prom.h> | 36 | #include <asm/prom.h> |
36 | #include <asm/rtas.h> | 37 | #include <asm/rtas.h> |
@@ -1865,17 +1866,32 @@ get_property(struct device_node *np, const char *name, int *lenp) | |||
1865 | EXPORT_SYMBOL(get_property); | 1866 | EXPORT_SYMBOL(get_property); |
1866 | 1867 | ||
1867 | /* | 1868 | /* |
1868 | * Add a property to a node | 1869 | * Add a property to a node. |
1869 | */ | 1870 | */ |
1870 | void | 1871 | int |
1871 | prom_add_property(struct device_node* np, struct property* prop) | 1872 | prom_add_property(struct device_node* np, struct property* prop) |
1872 | { | 1873 | { |
1873 | struct property **next = &np->properties; | 1874 | struct property **next; |
1874 | 1875 | ||
1875 | prop->next = NULL; | 1876 | prop->next = NULL; |
1876 | while (*next) | 1877 | write_lock(&devtree_lock); |
1878 | next = &np->properties; | ||
1879 | while (*next) { | ||
1880 | if (strcmp(prop->name, (*next)->name) == 0) { | ||
1881 | /* duplicate ! don't insert it */ | ||
1882 | write_unlock(&devtree_lock); | ||
1883 | return -1; | ||
1884 | } | ||
1877 | next = &(*next)->next; | 1885 | next = &(*next)->next; |
1886 | } | ||
1878 | *next = prop; | 1887 | *next = prop; |
1888 | write_unlock(&devtree_lock); | ||
1889 | |||
1890 | /* try to add to proc as well if it was initialized */ | ||
1891 | if (np->pde) | ||
1892 | proc_device_tree_add_prop(np->pde, prop); | ||
1893 | |||
1894 | return 0; | ||
1879 | } | 1895 | } |
1880 | 1896 | ||
1881 | #if 0 | 1897 | #if 0 |
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) |
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 6fd57f154197..fb117b74809e 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c | |||
@@ -49,6 +49,39 @@ static int property_read_proc(char *page, char **start, off_t off, | |||
49 | */ | 49 | */ |
50 | 50 | ||
51 | /* | 51 | /* |
52 | * Add a property to a node | ||
53 | */ | ||
54 | static struct proc_dir_entry * | ||
55 | __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | ||
56 | { | ||
57 | struct proc_dir_entry *ent; | ||
58 | |||
59 | /* | ||
60 | * Unfortunately proc_register puts each new entry | ||
61 | * at the beginning of the list. So we rearrange them. | ||
62 | */ | ||
63 | ent = create_proc_read_entry(pp->name, | ||
64 | strncmp(pp->name, "security-", 9) | ||
65 | ? S_IRUGO : S_IRUSR, de, | ||
66 | property_read_proc, pp); | ||
67 | if (ent == NULL) | ||
68 | return NULL; | ||
69 | |||
70 | if (!strncmp(pp->name, "security-", 9)) | ||
71 | ent->size = 0; /* don't leak number of password chars */ | ||
72 | else | ||
73 | ent->size = pp->length; | ||
74 | |||
75 | return ent; | ||
76 | } | ||
77 | |||
78 | |||
79 | void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) | ||
80 | { | ||
81 | __proc_device_tree_add_prop(pde, prop); | ||
82 | } | ||
83 | |||
84 | /* | ||
52 | * Process a node, adding entries for its children and its properties. | 85 | * Process a node, adding entries for its children and its properties. |
53 | */ | 86 | */ |
54 | void proc_device_tree_add_node(struct device_node *np, | 87 | void proc_device_tree_add_node(struct device_node *np, |
@@ -57,11 +90,9 @@ void proc_device_tree_add_node(struct device_node *np, | |||
57 | struct property *pp; | 90 | struct property *pp; |
58 | struct proc_dir_entry *ent; | 91 | struct proc_dir_entry *ent; |
59 | struct device_node *child; | 92 | struct device_node *child; |
60 | struct proc_dir_entry *list = NULL, **lastp; | ||
61 | const char *p; | 93 | const char *p; |
62 | 94 | ||
63 | set_node_proc_entry(np, de); | 95 | set_node_proc_entry(np, de); |
64 | lastp = &list; | ||
65 | for (child = NULL; (child = of_get_next_child(np, child));) { | 96 | for (child = NULL; (child = of_get_next_child(np, child));) { |
66 | p = strrchr(child->full_name, '/'); | 97 | p = strrchr(child->full_name, '/'); |
67 | if (!p) | 98 | if (!p) |
@@ -71,9 +102,6 @@ void proc_device_tree_add_node(struct device_node *np, | |||
71 | ent = proc_mkdir(p, de); | 102 | ent = proc_mkdir(p, de); |
72 | if (ent == 0) | 103 | if (ent == 0) |
73 | break; | 104 | break; |
74 | *lastp = ent; | ||
75 | ent->next = NULL; | ||
76 | lastp = &ent->next; | ||
77 | proc_device_tree_add_node(child, ent); | 105 | proc_device_tree_add_node(child, ent); |
78 | } | 106 | } |
79 | of_node_put(child); | 107 | of_node_put(child); |
@@ -84,7 +112,7 @@ void proc_device_tree_add_node(struct device_node *np, | |||
84 | * properties are quite unimportant for us though, thus we | 112 | * properties are quite unimportant for us though, thus we |
85 | * simply "skip" them here, but we do have to check. | 113 | * simply "skip" them here, but we do have to check. |
86 | */ | 114 | */ |
87 | for (ent = list; ent != NULL; ent = ent->next) | 115 | for (ent = de->subdir; ent != NULL; ent = ent->next) |
88 | if (!strcmp(ent->name, pp->name)) | 116 | if (!strcmp(ent->name, pp->name)) |
89 | break; | 117 | break; |
90 | if (ent != NULL) { | 118 | if (ent != NULL) { |
@@ -94,25 +122,10 @@ void proc_device_tree_add_node(struct device_node *np, | |||
94 | continue; | 122 | continue; |
95 | } | 123 | } |
96 | 124 | ||
97 | /* | 125 | ent = __proc_device_tree_add_prop(de, pp); |
98 | * Unfortunately proc_register puts each new entry | ||
99 | * at the beginning of the list. So we rearrange them. | ||
100 | */ | ||
101 | ent = create_proc_read_entry(pp->name, | ||
102 | strncmp(pp->name, "security-", 9) | ||
103 | ? S_IRUGO : S_IRUSR, de, | ||
104 | property_read_proc, pp); | ||
105 | if (ent == 0) | 126 | if (ent == 0) |
106 | break; | 127 | break; |
107 | if (!strncmp(pp->name, "security-", 9)) | ||
108 | ent->size = 0; /* don't leak number of password chars */ | ||
109 | else | ||
110 | ent->size = pp->length; | ||
111 | ent->next = NULL; | ||
112 | *lastp = ent; | ||
113 | lastp = &ent->next; | ||
114 | } | 128 | } |
115 | de->subdir = list; | ||
116 | } | 129 | } |
117 | 130 | ||
118 | /* | 131 | /* |
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 7587bf5f38c6..f999df1c5c90 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h | |||
@@ -203,7 +203,7 @@ extern int prom_n_addr_cells(struct device_node* np); | |||
203 | extern int prom_n_size_cells(struct device_node* np); | 203 | extern int prom_n_size_cells(struct device_node* np); |
204 | extern int prom_n_intr_cells(struct device_node* np); | 204 | extern int prom_n_intr_cells(struct device_node* np); |
205 | extern void prom_get_irq_senses(unsigned char *senses, int off, int max); | 205 | extern void prom_get_irq_senses(unsigned char *senses, int off, int max); |
206 | extern void prom_add_property(struct device_node* np, struct property* prop); | 206 | extern int prom_add_property(struct device_node* np, struct property* prop); |
207 | 207 | ||
208 | #ifdef CONFIG_PPC32 | 208 | #ifdef CONFIG_PPC32 |
209 | /* | 209 | /* |
diff --git a/include/asm-powerpc/smu.h b/include/asm-powerpc/smu.h index 959bad660233..76c29a9784dd 100644 --- a/include/asm-powerpc/smu.h +++ b/include/asm-powerpc/smu.h | |||
@@ -20,16 +20,52 @@ | |||
20 | /* | 20 | /* |
21 | * Partition info commands | 21 | * Partition info commands |
22 | * | 22 | * |
23 | * I do not know what those are for at this point | 23 | * These commands are used to retreive the sdb-partition-XX datas from |
24 | * the SMU. The lenght is always 2. First byte is the subcommand code | ||
25 | * and second byte is the partition ID. | ||
26 | * | ||
27 | * The reply is 6 bytes: | ||
28 | * | ||
29 | * - 0..1 : partition address | ||
30 | * - 2 : a byte containing the partition ID | ||
31 | * - 3 : length (maybe other bits are rest of header ?) | ||
32 | * | ||
33 | * The data must then be obtained with calls to another command: | ||
34 | * SMU_CMD_MISC_ee_GET_DATABLOCK_REC (described below). | ||
24 | */ | 35 | */ |
25 | #define SMU_CMD_PARTITION_COMMAND 0x3e | 36 | #define SMU_CMD_PARTITION_COMMAND 0x3e |
37 | #define SMU_CMD_PARTITION_LATEST 0x01 | ||
38 | #define SMU_CMD_PARTITION_BASE 0x02 | ||
39 | #define SMU_CMD_PARTITION_UPDATE 0x03 | ||
26 | 40 | ||
27 | 41 | ||
28 | /* | 42 | /* |
29 | * Fan control | 43 | * Fan control |
30 | * | 44 | * |
31 | * This is a "mux" for fan control commands, first byte is the | 45 | * This is a "mux" for fan control commands. The command seem to |
32 | * "sub" command. | 46 | * act differently based on the number of arguments. With 1 byte |
47 | * of argument, this seem to be queries for fans status, setpoint, | ||
48 | * etc..., while with 0xe arguments, we will set the fans speeds. | ||
49 | * | ||
50 | * Queries (1 byte arg): | ||
51 | * --------------------- | ||
52 | * | ||
53 | * arg=0x01: read RPM fans status | ||
54 | * arg=0x02: read RPM fans setpoint | ||
55 | * arg=0x11: read PWM fans status | ||
56 | * arg=0x12: read PWM fans setpoint | ||
57 | * | ||
58 | * the "status" queries return the current speed while the "setpoint" ones | ||
59 | * return the programmed/target speed. It _seems_ that the result is a bit | ||
60 | * mask in the first byte of active/available fans, followed by 6 words (16 | ||
61 | * bits) containing the requested speed. | ||
62 | * | ||
63 | * Setpoint (14 bytes arg): | ||
64 | * ------------------------ | ||
65 | * | ||
66 | * first arg byte is 0 for RPM fans and 0x10 for PWM. Second arg byte is the | ||
67 | * mask of fans affected by the command. Followed by 6 words containing the | ||
68 | * setpoint value for selected fans in the mask (or 0 if mask value is 0) | ||
33 | */ | 69 | */ |
34 | #define SMU_CMD_FAN_COMMAND 0x4a | 70 | #define SMU_CMD_FAN_COMMAND 0x4a |
35 | 71 | ||
@@ -156,6 +192,14 @@ | |||
156 | #define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN" | 192 | #define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN" |
157 | #define SMU_CMD_POWER_VOLTAGE_SLEW "VSLEW" | 193 | #define SMU_CMD_POWER_VOLTAGE_SLEW "VSLEW" |
158 | 194 | ||
195 | /* | ||
196 | * Read ADC sensors | ||
197 | * | ||
198 | * This command takes one byte of parameter: the sensor ID (or "reg" | ||
199 | * value in the device-tree) and returns a 16 bits value | ||
200 | */ | ||
201 | #define SMU_CMD_READ_ADC 0xd8 | ||
202 | |||
159 | /* Misc commands | 203 | /* Misc commands |
160 | * | 204 | * |
161 | * This command seem to be a grab bag of various things | 205 | * This command seem to be a grab bag of various things |
@@ -176,6 +220,25 @@ | |||
176 | * Misc commands | 220 | * Misc commands |
177 | * | 221 | * |
178 | * This command seem to be a grab bag of various things | 222 | * This command seem to be a grab bag of various things |
223 | * | ||
224 | * SMU_CMD_MISC_ee_GET_DATABLOCK_REC is used, among others, to | ||
225 | * transfer blocks of data from the SMU. So far, I've decrypted it's | ||
226 | * usage to retreive partition data. In order to do that, you have to | ||
227 | * break your transfer in "chunks" since that command cannot transfer | ||
228 | * more than a chunk at a time. The chunk size used by OF is 0xe bytes, | ||
229 | * but it seems that the darwin driver will let you do 0x1e bytes if | ||
230 | * your "PMU" version is >= 0x30. You can get the "PMU" version apparently | ||
231 | * either in the last 16 bits of property "smu-version-pmu" or as the 16 | ||
232 | * bytes at offset 1 of "smu-version-info" | ||
233 | * | ||
234 | * For each chunk, the command takes 7 bytes of arguments: | ||
235 | * byte 0: subcommand code (0x02) | ||
236 | * byte 1: 0x04 (always, I don't know what it means, maybe the address | ||
237 | * space to use or some other nicety. It's hard coded in OF) | ||
238 | * byte 2..5: SMU address of the chunk (big endian 32 bits) | ||
239 | * byte 6: size to transfer (up to max chunk size) | ||
240 | * | ||
241 | * The data is returned directly | ||
179 | */ | 242 | */ |
180 | #define SMU_CMD_MISC_ee_COMMAND 0xee | 243 | #define SMU_CMD_MISC_ee_COMMAND 0xee |
181 | #define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02 | 244 | #define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02 |
@@ -353,21 +416,26 @@ struct smu_sdbp_header { | |||
353 | __u8 flags; | 416 | __u8 flags; |
354 | }; | 417 | }; |
355 | 418 | ||
356 | /* | 419 | |
357 | * 32 bits integers are usually encoded with 2x16 bits swapped, | 420 | /* |
358 | * this demangles them | 421 | * demangle 16 and 32 bits integer in some SMU partitions |
422 | * (currently, afaik, this concerns only the FVT partition | ||
423 | * (0x12) | ||
359 | */ | 424 | */ |
360 | #define SMU_U32_MIX(x) ((((x) << 16) & 0xffff0000u) | (((x) >> 16) & 0xffffu)) | 425 | #define SMU_U16_MIX(x) le16_to_cpu(x); |
426 | #define SMU_U32_MIX(x) ((((x) & 0xff00ff00u) >> 8)|(((x) & 0x00ff00ffu) << 8)) | ||
427 | |||
361 | 428 | ||
362 | /* This is the definition of the SMU sdb-partition-0x12 table (called | 429 | /* This is the definition of the SMU sdb-partition-0x12 table (called |
363 | * CPU F/V/T operating points in Darwin). The definition for all those | 430 | * CPU F/V/T operating points in Darwin). The definition for all those |
364 | * SMU tables should be moved to some separate file | 431 | * SMU tables should be moved to some separate file |
365 | */ | 432 | */ |
366 | #define SMU_SDB_FVT_ID 0x12 | 433 | #define SMU_SDB_FVT_ID 0x12 |
367 | 434 | ||
368 | struct smu_sdbp_fvt { | 435 | struct smu_sdbp_fvt { |
369 | __u32 sysclk; /* Base SysClk frequency in Hz for | 436 | __u32 sysclk; /* Base SysClk frequency in Hz for |
370 | * this operating point | 437 | * this operating point. Value need to |
438 | * be unmixed with SMU_U32_MIX() | ||
371 | */ | 439 | */ |
372 | __u8 pad; | 440 | __u8 pad; |
373 | __u8 maxtemp; /* Max temp. supported by this | 441 | __u8 maxtemp; /* Max temp. supported by this |
@@ -376,10 +444,73 @@ struct smu_sdbp_fvt { | |||
376 | 444 | ||
377 | __u16 volts[3]; /* CPU core voltage for the 3 | 445 | __u16 volts[3]; /* CPU core voltage for the 3 |
378 | * PowerTune modes, a mode with | 446 | * PowerTune modes, a mode with |
379 | * 0V = not supported. | 447 | * 0V = not supported. Value need |
448 | * to be unmixed with SMU_U16_MIX() | ||
380 | */ | 449 | */ |
381 | }; | 450 | }; |
382 | 451 | ||
452 | /* This partition contains voltage & current sensor calibration | ||
453 | * informations | ||
454 | */ | ||
455 | #define SMU_SDB_CPUVCP_ID 0x21 | ||
456 | |||
457 | struct smu_sdbp_cpuvcp { | ||
458 | __u16 volt_scale; /* u4.12 fixed point */ | ||
459 | __s16 volt_offset; /* s4.12 fixed point */ | ||
460 | __u16 curr_scale; /* u4.12 fixed point */ | ||
461 | __s16 curr_offset; /* s4.12 fixed point */ | ||
462 | __s32 power_quads[3]; /* s4.28 fixed point */ | ||
463 | }; | ||
464 | |||
465 | /* This partition contains CPU thermal diode calibration | ||
466 | */ | ||
467 | #define SMU_SDB_CPUDIODE_ID 0x18 | ||
468 | |||
469 | struct smu_sdbp_cpudiode { | ||
470 | __u16 m_value; /* u1.15 fixed point */ | ||
471 | __s16 b_value; /* s10.6 fixed point */ | ||
472 | |||
473 | }; | ||
474 | |||
475 | /* This partition contains Slots power calibration | ||
476 | */ | ||
477 | #define SMU_SDB_SLOTSPOW_ID 0x78 | ||
478 | |||
479 | struct smu_sdbp_slotspow { | ||
480 | __u16 pow_scale; /* u4.12 fixed point */ | ||
481 | __s16 pow_offset; /* s4.12 fixed point */ | ||
482 | }; | ||
483 | |||
484 | /* This partition contains machine specific version information about | ||
485 | * the sensor/control layout | ||
486 | */ | ||
487 | #define SMU_SDB_SENSORTREE_ID 0x25 | ||
488 | |||
489 | struct smu_sdbp_sensortree { | ||
490 | u8 model_id; | ||
491 | u8 unknown[3]; | ||
492 | }; | ||
493 | |||
494 | /* This partition contains CPU thermal control PID informations. So far | ||
495 | * only single CPU machines have been seen with an SMU, so we assume this | ||
496 | * carries only informations for those | ||
497 | */ | ||
498 | #define SMU_SDB_CPUPIDDATA_ID 0x17 | ||
499 | |||
500 | struct smu_sdbp_cpupiddata { | ||
501 | u8 unknown1; | ||
502 | u8 target_temp_delta; | ||
503 | u8 unknown2; | ||
504 | u8 history_len; | ||
505 | s16 power_adj; | ||
506 | u16 max_power; | ||
507 | s32 gp,gr,gd; | ||
508 | }; | ||
509 | |||
510 | |||
511 | /* Other partitions without known structures */ | ||
512 | #define SMU_SDB_DEBUG_SWITCHES_ID 0x05 | ||
513 | |||
383 | #ifdef __KERNEL__ | 514 | #ifdef __KERNEL__ |
384 | /* | 515 | /* |
385 | * This returns the pointer to an SMU "sdb" partition data or NULL | 516 | * This returns the pointer to an SMU "sdb" partition data or NULL |
@@ -423,8 +554,10 @@ struct smu_user_cmd_hdr | |||
423 | __u32 cmdtype; | 554 | __u32 cmdtype; |
424 | #define SMU_CMDTYPE_SMU 0 /* SMU command */ | 555 | #define SMU_CMDTYPE_SMU 0 /* SMU command */ |
425 | #define SMU_CMDTYPE_WANTS_EVENTS 1 /* switch fd to events mode */ | 556 | #define SMU_CMDTYPE_WANTS_EVENTS 1 /* switch fd to events mode */ |
557 | #define SMU_CMDTYPE_GET_PARTITION 2 /* retreive an sdb partition */ | ||
426 | 558 | ||
427 | __u8 cmd; /* SMU command byte */ | 559 | __u8 cmd; /* SMU command byte */ |
560 | __u8 pad[3]; /* padding */ | ||
428 | __u32 data_len; /* Lenght of data following */ | 561 | __u32 data_len; /* Lenght of data following */ |
429 | }; | 562 | }; |
430 | 563 | ||
diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h index 75c0637acdc8..3e39827ed566 100644 --- a/include/asm-ppc/prom.h +++ b/include/asm-ppc/prom.h | |||
@@ -93,7 +93,7 @@ extern int device_is_compatible(struct device_node *device, const char *); | |||
93 | extern int machine_is_compatible(const char *compat); | 93 | extern int machine_is_compatible(const char *compat); |
94 | extern unsigned char *get_property(struct device_node *node, const char *name, | 94 | extern unsigned char *get_property(struct device_node *node, const char *name, |
95 | int *lenp); | 95 | int *lenp); |
96 | extern void prom_add_property(struct device_node* np, struct property* prop); | 96 | extern int prom_add_property(struct device_node* np, struct property* prop); |
97 | extern void prom_get_irq_senses(unsigned char *, int, int); | 97 | extern void prom_get_irq_senses(unsigned char *, int, int); |
98 | extern int prom_n_addr_cells(struct device_node* np); | 98 | extern int prom_n_addr_cells(struct device_node* np); |
99 | extern int prom_n_size_cells(struct device_node* np); | 99 | extern int prom_n_size_cells(struct device_node* np); |
diff --git a/include/asm-ppc64/prom.h b/include/asm-ppc64/prom.h index bdb47174ff0e..76bb0266d67c 100644 --- a/include/asm-ppc64/prom.h +++ b/include/asm-ppc64/prom.h | |||
@@ -213,6 +213,6 @@ extern int prom_n_addr_cells(struct device_node* np); | |||
213 | extern int prom_n_size_cells(struct device_node* np); | 213 | extern int prom_n_size_cells(struct device_node* np); |
214 | extern int prom_n_intr_cells(struct device_node* np); | 214 | extern int prom_n_intr_cells(struct device_node* np); |
215 | extern void prom_get_irq_senses(unsigned char *senses, int off, int max); | 215 | extern void prom_get_irq_senses(unsigned char *senses, int off, int max); |
216 | extern void prom_add_property(struct device_node* np, struct property* prop); | 216 | extern int prom_add_property(struct device_node* np, struct property* prop); |
217 | 217 | ||
218 | #endif /* _PPC64_PROM_H */ | 218 | #endif /* _PPC64_PROM_H */ |
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 0563581e3a02..65ceeaa30652 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -139,15 +139,12 @@ extern void proc_tty_unregister_driver(struct tty_driver *driver); | |||
139 | /* | 139 | /* |
140 | * proc_devtree.c | 140 | * proc_devtree.c |
141 | */ | 141 | */ |
142 | #ifdef CONFIG_PROC_DEVICETREE | ||
142 | struct device_node; | 143 | struct device_node; |
144 | struct property; | ||
143 | extern void proc_device_tree_init(void); | 145 | extern void proc_device_tree_init(void); |
144 | #ifdef CONFIG_PROC_DEVICETREE | ||
145 | extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *); | 146 | extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *); |
146 | #else /* !CONFIG_PROC_DEVICETREE */ | 147 | extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop); |
147 | static inline void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *pde) | ||
148 | { | ||
149 | return; | ||
150 | } | ||
151 | #endif /* CONFIG_PROC_DEVICETREE */ | 148 | #endif /* CONFIG_PROC_DEVICETREE */ |
152 | 149 | ||
153 | extern struct proc_dir_entry *proc_symlink(const char *, | 150 | extern struct proc_dir_entry *proc_symlink(const char *, |