diff options
| -rw-r--r-- | arch/powerpc/platforms/powermac/Makefile | 3 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powermac/feature.c | 46 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powermac/low_i2c.c | 294 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powermac/pfunc_base.c | 405 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powermac/pfunc_core.c | 989 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powermac/smp.c | 48 | ||||
| -rw-r--r-- | drivers/macintosh/via-pmu.c | 10 | ||||
| -rw-r--r-- | include/asm-powerpc/pmac_feature.h | 19 | ||||
| -rw-r--r-- | include/asm-powerpc/pmac_low_i2c.h | 3 | ||||
| -rw-r--r-- | include/asm-powerpc/pmac_pfunc.h | 253 |
10 files changed, 2028 insertions, 42 deletions
diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index faa1a2c82bcf..78093d7f97af 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | CFLAGS_bootx_init.o += -fPIC | 1 | CFLAGS_bootx_init.o += -fPIC |
| 2 | 2 | ||
| 3 | obj-y += pic.o setup.o time.o feature.o pci.o \ | 3 | obj-y += pic.o setup.o time.o feature.o pci.o \ |
| 4 | sleep.o low_i2c.o cache.o | 4 | sleep.o low_i2c.o cache.o pfunc_core.o \ |
| 5 | pfunc_base.o | ||
| 5 | obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o | 6 | obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o |
| 6 | obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o | 7 | obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o |
| 7 | obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o | 8 | obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o |
diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index b271b11583ac..558dd0692092 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c | |||
| @@ -58,12 +58,11 @@ extern int powersave_lowspeed; | |||
| 58 | extern int powersave_nap; | 58 | extern int powersave_nap; |
| 59 | extern struct device_node *k2_skiplist[2]; | 59 | extern struct device_node *k2_skiplist[2]; |
| 60 | 60 | ||
| 61 | |||
| 62 | /* | 61 | /* |
| 63 | * We use a single global lock to protect accesses. Each driver has | 62 | * We use a single global lock to protect accesses. Each driver has |
| 64 | * to take care of its own locking | 63 | * to take care of its own locking |
| 65 | */ | 64 | */ |
| 66 | static DEFINE_SPINLOCK(feature_lock); | 65 | DEFINE_SPINLOCK(feature_lock); |
| 67 | 66 | ||
| 68 | #define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); | 67 | #define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); |
| 69 | #define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); | 68 | #define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); |
| @@ -106,22 +105,12 @@ static const char *macio_names[] = | |||
| 106 | }; | 105 | }; |
| 107 | 106 | ||
| 108 | 107 | ||
| 108 | struct device_node *uninorth_node; | ||
| 109 | u32 __iomem *uninorth_base; | ||
| 109 | 110 | ||
| 110 | /* | ||
| 111 | * Uninorth reg. access. Note that Uni-N regs are big endian | ||
| 112 | */ | ||
| 113 | |||
| 114 | #define UN_REG(r) (uninorth_base + ((r) >> 2)) | ||
| 115 | #define UN_IN(r) (in_be32(UN_REG(r))) | ||
| 116 | #define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) | ||
| 117 | #define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) | ||
| 118 | #define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) | ||
| 119 | |||
| 120 | static struct device_node *uninorth_node; | ||
| 121 | static u32 __iomem *uninorth_base; | ||
| 122 | static u32 uninorth_rev; | 111 | static u32 uninorth_rev; |
| 123 | static int uninorth_maj; | 112 | static int uninorth_maj; |
| 124 | static void __iomem *u3_ht; | 113 | static void __iomem *u3_ht_base; |
| 125 | 114 | ||
| 126 | /* | 115 | /* |
| 127 | * For each motherboard family, we have a table of functions pointers | 116 | * For each motherboard family, we have a table of functions pointers |
| @@ -1560,8 +1549,10 @@ void g5_phy_disable_cpu1(void) | |||
| 1560 | 1549 | ||
| 1561 | #ifndef CONFIG_POWER4 | 1550 | #ifndef CONFIG_POWER4 |
| 1562 | 1551 | ||
| 1563 | static void | 1552 | |
| 1564 | keylargo_shutdown(struct macio_chip *macio, int sleep_mode) | 1553 | #ifdef CONFIG_PM |
| 1554 | |||
| 1555 | static void keylargo_shutdown(struct macio_chip *macio, int sleep_mode) | ||
| 1565 | { | 1556 | { |
| 1566 | u32 temp; | 1557 | u32 temp; |
| 1567 | 1558 | ||
| @@ -1614,8 +1605,7 @@ keylargo_shutdown(struct macio_chip *macio, int sleep_mode) | |||
| 1614 | (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); | 1605 | (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); |
| 1615 | } | 1606 | } |
| 1616 | 1607 | ||
| 1617 | static void | 1608 | static void pangea_shutdown(struct macio_chip *macio, int sleep_mode) |
| 1618 | pangea_shutdown(struct macio_chip *macio, int sleep_mode) | ||
| 1619 | { | 1609 | { |
| 1620 | u32 temp; | 1610 | u32 temp; |
| 1621 | 1611 | ||
| @@ -1648,8 +1638,7 @@ pangea_shutdown(struct macio_chip *macio, int sleep_mode) | |||
| 1648 | (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); | 1638 | (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); |
| 1649 | } | 1639 | } |
| 1650 | 1640 | ||
| 1651 | static void | 1641 | static void intrepid_shutdown(struct macio_chip *macio, int sleep_mode) |
| 1652 | intrepid_shutdown(struct macio_chip *macio, int sleep_mode) | ||
| 1653 | { | 1642 | { |
| 1654 | u32 temp; | 1643 | u32 temp; |
| 1655 | 1644 | ||
| @@ -1833,6 +1822,8 @@ core99_wake_up(void) | |||
| 1833 | return 0; | 1822 | return 0; |
| 1834 | } | 1823 | } |
| 1835 | 1824 | ||
| 1825 | #endif /* CONFIG_PM */ | ||
| 1826 | |||
| 1836 | static long | 1827 | static long |
| 1837 | core99_sleep_state(struct device_node *node, long param, long value) | 1828 | core99_sleep_state(struct device_node *node, long param, long value) |
| 1838 | { | 1829 | { |
| @@ -1854,10 +1845,13 @@ core99_sleep_state(struct device_node *node, long param, long value) | |||
| 1854 | if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) | 1845 | if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) |
| 1855 | return -EPERM; | 1846 | return -EPERM; |
| 1856 | 1847 | ||
| 1848 | #ifdef CONFIG_PM | ||
| 1857 | if (value == 1) | 1849 | if (value == 1) |
| 1858 | return core99_sleep(); | 1850 | return core99_sleep(); |
| 1859 | else if (value == 0) | 1851 | else if (value == 0) |
| 1860 | return core99_wake_up(); | 1852 | return core99_wake_up(); |
| 1853 | |||
| 1854 | #endif /* CONFIG_PM */ | ||
| 1861 | return 0; | 1855 | return 0; |
| 1862 | } | 1856 | } |
| 1863 | 1857 | ||
| @@ -1981,7 +1975,9 @@ static struct feature_table_entry core99_features[] = { | |||
| 1981 | { PMAC_FTR_USB_ENABLE, core99_usb_enable }, | 1975 | { PMAC_FTR_USB_ENABLE, core99_usb_enable }, |
| 1982 | { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, | 1976 | { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, |
| 1983 | { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, | 1977 | { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, |
| 1978 | #ifdef CONFIG_PM | ||
| 1984 | { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, | 1979 | { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, |
| 1980 | #endif | ||
| 1985 | #ifdef CONFIG_SMP | 1981 | #ifdef CONFIG_SMP |
| 1986 | { PMAC_FTR_RESET_CPU, core99_reset_cpu }, | 1982 | { PMAC_FTR_RESET_CPU, core99_reset_cpu }, |
| 1987 | #endif /* CONFIG_SMP */ | 1983 | #endif /* CONFIG_SMP */ |
| @@ -2572,7 +2568,7 @@ static void __init probe_uninorth(void) | |||
| 2572 | uninorth_base = ioremap(address, 0x40000); | 2568 | uninorth_base = ioremap(address, 0x40000); |
| 2573 | uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); | 2569 | uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); |
| 2574 | if (uninorth_maj == 3 || uninorth_maj == 4) | 2570 | if (uninorth_maj == 3 || uninorth_maj == 4) |
| 2575 | u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); | 2571 | u3_ht_base = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); |
| 2576 | 2572 | ||
| 2577 | printk(KERN_INFO "Found %s memory controller & host bridge" | 2573 | printk(KERN_INFO "Found %s memory controller & host bridge" |
| 2578 | " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" : | 2574 | " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" : |
| @@ -2921,9 +2917,9 @@ void __init pmac_check_ht_link(void) | |||
| 2921 | u8 px_bus, px_devfn; | 2917 | u8 px_bus, px_devfn; |
| 2922 | struct pci_controller *px_hose; | 2918 | struct pci_controller *px_hose; |
| 2923 | 2919 | ||
| 2924 | (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); | 2920 | (void)in_be32(u3_ht_base + U3_HT_LINK_COMMAND); |
| 2925 | ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); | 2921 | ucfg = cfg = in_be32(u3_ht_base + U3_HT_LINK_CONFIG); |
| 2926 | ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); | 2922 | ufreq = freq = in_be32(u3_ht_base + U3_HT_LINK_FREQ); |
| 2927 | dump_HT_speeds("U3 HyperTransport", cfg, freq); | 2923 | dump_HT_speeds("U3 HyperTransport", cfg, freq); |
| 2928 | 2924 | ||
| 2929 | pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); | 2925 | pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); |
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index a25e447f907f..535c802b369f 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c | |||
| @@ -49,6 +49,7 @@ | |||
| 49 | #include <asm/prom.h> | 49 | #include <asm/prom.h> |
| 50 | #include <asm/machdep.h> | 50 | #include <asm/machdep.h> |
| 51 | #include <asm/smu.h> | 51 | #include <asm/smu.h> |
| 52 | #include <asm/pmac_pfunc.h> | ||
| 52 | #include <asm/pmac_low_i2c.h> | 53 | #include <asm/pmac_low_i2c.h> |
| 53 | 54 | ||
| 54 | #ifdef DEBUG | 55 | #ifdef DEBUG |
| @@ -1162,9 +1163,291 @@ int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, | |||
| 1162 | } | 1163 | } |
| 1163 | EXPORT_SYMBOL_GPL(pmac_i2c_xfer); | 1164 | EXPORT_SYMBOL_GPL(pmac_i2c_xfer); |
| 1164 | 1165 | ||
| 1166 | /* some quirks for platform function decoding */ | ||
| 1167 | enum { | ||
| 1168 | pmac_i2c_quirk_invmask = 0x00000001u, | ||
| 1169 | }; | ||
| 1170 | |||
| 1171 | static void pmac_i2c_devscan(void (*callback)(struct device_node *dev, | ||
| 1172 | int quirks)) | ||
| 1173 | { | ||
| 1174 | struct pmac_i2c_bus *bus; | ||
| 1175 | struct device_node *np; | ||
| 1176 | static struct whitelist_ent { | ||
| 1177 | char *name; | ||
| 1178 | char *compatible; | ||
| 1179 | int quirks; | ||
| 1180 | } whitelist[] = { | ||
| 1181 | /* XXX Study device-tree's & apple drivers are get the quirks | ||
| 1182 | * right ! | ||
| 1183 | */ | ||
| 1184 | { "i2c-hwclock", NULL, pmac_i2c_quirk_invmask }, | ||
| 1185 | { "i2c-cpu-voltage", NULL, 0}, | ||
| 1186 | { "temp-monitor", NULL, 0 }, | ||
| 1187 | { "supply-monitor", NULL, 0 }, | ||
| 1188 | { NULL, NULL, 0 }, | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | /* Only some devices need to have platform functions instanciated | ||
| 1192 | * here. For now, we have a table. Others, like 9554 i2c GPIOs used | ||
| 1193 | * on Xserve, if we ever do a driver for them, will use their own | ||
| 1194 | * platform function instance | ||
| 1195 | */ | ||
| 1196 | list_for_each_entry(bus, &pmac_i2c_busses, link) { | ||
| 1197 | for (np = NULL; | ||
| 1198 | (np = of_get_next_child(bus->busnode, np)) != NULL;) { | ||
| 1199 | struct whitelist_ent *p; | ||
| 1200 | /* If multibus, check if device is on that bus */ | ||
| 1201 | if (bus->flags & pmac_i2c_multibus) | ||
| 1202 | if (bus != pmac_i2c_find_bus(np)) | ||
| 1203 | continue; | ||
| 1204 | for (p = whitelist; p->name != NULL; p++) { | ||
| 1205 | if (strcmp(np->name, p->name)) | ||
| 1206 | continue; | ||
| 1207 | if (p->compatible && | ||
| 1208 | !device_is_compatible(np, p->compatible)) | ||
| 1209 | continue; | ||
| 1210 | callback(np, p->quirks); | ||
| 1211 | break; | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | } | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | #define MAX_I2C_DATA 64 | ||
| 1218 | |||
| 1219 | struct pmac_i2c_pf_inst | ||
| 1220 | { | ||
| 1221 | struct pmac_i2c_bus *bus; | ||
| 1222 | u8 addr; | ||
| 1223 | u8 buffer[MAX_I2C_DATA]; | ||
| 1224 | u8 scratch[MAX_I2C_DATA]; | ||
| 1225 | int bytes; | ||
| 1226 | int quirks; | ||
| 1227 | }; | ||
| 1228 | |||
| 1229 | static void* pmac_i2c_do_begin(struct pmf_function *func, struct pmf_args *args) | ||
| 1230 | { | ||
| 1231 | struct pmac_i2c_pf_inst *inst; | ||
| 1232 | struct pmac_i2c_bus *bus; | ||
| 1233 | |||
| 1234 | bus = pmac_i2c_find_bus(func->node); | ||
| 1235 | if (bus == NULL) { | ||
| 1236 | printk(KERN_ERR "low_i2c: Can't find bus for %s (pfunc)\n", | ||
| 1237 | func->node->full_name); | ||
| 1238 | return NULL; | ||
| 1239 | } | ||
| 1240 | if (pmac_i2c_open(bus, 0)) { | ||
| 1241 | printk(KERN_ERR "low_i2c: Can't open i2c bus for %s (pfunc)\n", | ||
| 1242 | func->node->full_name); | ||
| 1243 | return NULL; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | /* XXX might need GFP_ATOMIC when called during the suspend process, | ||
| 1247 | * but then, there are already lots of issues with suspending when | ||
| 1248 | * near OOM that need to be resolved, the allocator itself should | ||
| 1249 | * probably make GFP_NOIO implicit during suspend | ||
| 1250 | */ | ||
| 1251 | inst = kzalloc(sizeof(struct pmac_i2c_pf_inst), GFP_KERNEL); | ||
| 1252 | if (inst == NULL) { | ||
| 1253 | pmac_i2c_close(bus); | ||
| 1254 | return NULL; | ||
| 1255 | } | ||
| 1256 | inst->bus = bus; | ||
| 1257 | inst->addr = pmac_i2c_get_dev_addr(func->node); | ||
| 1258 | inst->quirks = (int)(long)func->driver_data; | ||
| 1259 | return inst; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | static void pmac_i2c_do_end(struct pmf_function *func, void *instdata) | ||
| 1263 | { | ||
| 1264 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1265 | |||
| 1266 | if (inst == NULL) | ||
| 1267 | return; | ||
| 1268 | pmac_i2c_close(inst->bus); | ||
| 1269 | if (inst) | ||
| 1270 | kfree(inst); | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | static int pmac_i2c_do_read(PMF_STD_ARGS, u32 len) | ||
| 1274 | { | ||
| 1275 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1276 | |||
| 1277 | inst->bytes = len; | ||
| 1278 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 0, 0, | ||
| 1279 | inst->buffer, len); | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | static int pmac_i2c_do_write(PMF_STD_ARGS, u32 len, const u8 *data) | ||
| 1283 | { | ||
| 1284 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1285 | |||
| 1286 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0, | ||
| 1287 | (u8 *)data, len); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | /* This function is used to do the masking & OR'ing for the "rmw" type | ||
| 1291 | * callbacks. Ze should apply the mask and OR in the values in the | ||
| 1292 | * buffer before writing back. The problem is that it seems that | ||
| 1293 | * various darwin drivers implement the mask/or differently, thus | ||
| 1294 | * we need to check the quirks first | ||
| 1295 | */ | ||
| 1296 | static void pmac_i2c_do_apply_rmw(struct pmac_i2c_pf_inst *inst, | ||
| 1297 | u32 len, const u8 *mask, const u8 *val) | ||
| 1298 | { | ||
| 1299 | int i; | ||
| 1300 | |||
| 1301 | if (inst->quirks & pmac_i2c_quirk_invmask) { | ||
| 1302 | for (i = 0; i < len; i ++) | ||
| 1303 | inst->scratch[i] = (inst->buffer[i] & mask[i]) | val[i]; | ||
| 1304 | } else { | ||
| 1305 | for (i = 0; i < len; i ++) | ||
| 1306 | inst->scratch[i] = (inst->buffer[i] & ~mask[i]) | ||
| 1307 | | (val[i] & mask[i]); | ||
| 1308 | } | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | static int pmac_i2c_do_rmw(PMF_STD_ARGS, u32 masklen, u32 valuelen, | ||
| 1312 | u32 totallen, const u8 *maskdata, | ||
| 1313 | const u8 *valuedata) | ||
| 1314 | { | ||
| 1315 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1316 | |||
| 1317 | if (masklen > inst->bytes || valuelen > inst->bytes || | ||
| 1318 | totallen > inst->bytes || valuelen > masklen) | ||
| 1319 | return -EINVAL; | ||
| 1320 | |||
| 1321 | pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata); | ||
| 1322 | |||
| 1323 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0, | ||
| 1324 | inst->scratch, totallen); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | static int pmac_i2c_do_read_sub(PMF_STD_ARGS, u8 subaddr, u32 len) | ||
| 1328 | { | ||
| 1329 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1330 | |||
| 1331 | inst->bytes = len; | ||
| 1332 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 1, subaddr, | ||
| 1333 | inst->buffer, len); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | static int pmac_i2c_do_write_sub(PMF_STD_ARGS, u8 subaddr, u32 len, | ||
| 1337 | const u8 *data) | ||
| 1338 | { | ||
| 1339 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1340 | |||
| 1341 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1, | ||
| 1342 | subaddr, (u8 *)data, len); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | static int pmac_i2c_do_set_mode(PMF_STD_ARGS, int mode) | ||
| 1346 | { | ||
| 1347 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1348 | |||
| 1349 | return pmac_i2c_setmode(inst->bus, mode); | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | static int pmac_i2c_do_rmw_sub(PMF_STD_ARGS, u8 subaddr, u32 masklen, | ||
| 1353 | u32 valuelen, u32 totallen, const u8 *maskdata, | ||
| 1354 | const u8 *valuedata) | ||
| 1355 | { | ||
| 1356 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1357 | |||
| 1358 | if (masklen > inst->bytes || valuelen > inst->bytes || | ||
| 1359 | totallen > inst->bytes || valuelen > masklen) | ||
| 1360 | return -EINVAL; | ||
| 1361 | |||
| 1362 | pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata); | ||
| 1363 | |||
| 1364 | return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1, | ||
| 1365 | subaddr, inst->scratch, totallen); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | static int pmac_i2c_do_mask_and_comp(PMF_STD_ARGS, u32 len, | ||
| 1369 | const u8 *maskdata, | ||
| 1370 | const u8 *valuedata) | ||
| 1371 | { | ||
| 1372 | struct pmac_i2c_pf_inst *inst = instdata; | ||
| 1373 | int i, match; | ||
| 1374 | |||
| 1375 | /* Get return value pointer, it's assumed to be a u32 */ | ||
| 1376 | if (!args || !args->count || !args->u[0].p) | ||
| 1377 | return -EINVAL; | ||
| 1378 | |||
| 1379 | /* Check buffer */ | ||
| 1380 | if (len > inst->bytes) | ||
| 1381 | return -EINVAL; | ||
| 1382 | |||
| 1383 | for (i = 0, match = 1; match && i < len; i ++) | ||
| 1384 | if ((inst->buffer[i] & maskdata[i]) != valuedata[i]) | ||
| 1385 | match = 0; | ||
| 1386 | *args->u[0].p = match; | ||
| 1387 | return 0; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | static int pmac_i2c_do_delay(PMF_STD_ARGS, u32 duration) | ||
| 1391 | { | ||
| 1392 | msleep((duration + 999) / 1000); | ||
| 1393 | return 0; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | |||
| 1397 | static struct pmf_handlers pmac_i2c_pfunc_handlers = { | ||
| 1398 | .begin = pmac_i2c_do_begin, | ||
| 1399 | .end = pmac_i2c_do_end, | ||
| 1400 | .read_i2c = pmac_i2c_do_read, | ||
| 1401 | .write_i2c = pmac_i2c_do_write, | ||
| 1402 | .rmw_i2c = pmac_i2c_do_rmw, | ||
| 1403 | .read_i2c_sub = pmac_i2c_do_read_sub, | ||
| 1404 | .write_i2c_sub = pmac_i2c_do_write_sub, | ||
| 1405 | .rmw_i2c_sub = pmac_i2c_do_rmw_sub, | ||
| 1406 | .set_i2c_mode = pmac_i2c_do_set_mode, | ||
| 1407 | .mask_and_compare = pmac_i2c_do_mask_and_comp, | ||
| 1408 | .delay = pmac_i2c_do_delay, | ||
| 1409 | }; | ||
| 1410 | |||
| 1411 | static void __init pmac_i2c_dev_create(struct device_node *np, int quirks) | ||
| 1412 | { | ||
| 1413 | DBG("dev_create(%s)\n", np->full_name); | ||
| 1414 | |||
| 1415 | pmf_register_driver(np, &pmac_i2c_pfunc_handlers, | ||
| 1416 | (void *)(long)quirks); | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | static void __init pmac_i2c_dev_init(struct device_node *np, int quirks) | ||
| 1420 | { | ||
| 1421 | DBG("dev_create(%s)\n", np->full_name); | ||
| 1422 | |||
| 1423 | pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_INIT, NULL); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static void pmac_i2c_dev_suspend(struct device_node *np, int quirks) | ||
| 1427 | { | ||
| 1428 | DBG("dev_suspend(%s)\n", np->full_name); | ||
| 1429 | pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_SLEEP, NULL); | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | static void pmac_i2c_dev_resume(struct device_node *np, int quirks) | ||
| 1433 | { | ||
| 1434 | DBG("dev_resume(%s)\n", np->full_name); | ||
| 1435 | pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_WAKE, NULL); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | void pmac_pfunc_i2c_suspend(void) | ||
| 1439 | { | ||
| 1440 | pmac_i2c_devscan(pmac_i2c_dev_suspend); | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | void pmac_pfunc_i2c_resume(void) | ||
| 1444 | { | ||
| 1445 | pmac_i2c_devscan(pmac_i2c_dev_resume); | ||
| 1446 | } | ||
| 1447 | |||
| 1165 | /* | 1448 | /* |
| 1166 | * Initialize us: probe all i2c busses on the machine and instantiate | 1449 | * Initialize us: probe all i2c busses on the machine, instantiate |
| 1167 | * busses. | 1450 | * busses and platform functions as needed. |
| 1168 | */ | 1451 | */ |
| 1169 | /* This is non-static as it might be called early by smp code */ | 1452 | /* This is non-static as it might be called early by smp code */ |
| 1170 | int __init pmac_i2c_init(void) | 1453 | int __init pmac_i2c_init(void) |
| @@ -1187,6 +1470,10 @@ int __init pmac_i2c_init(void) | |||
| 1187 | /* Probe SMU i2c busses */ | 1470 | /* Probe SMU i2c busses */ |
| 1188 | smu_i2c_probe(); | 1471 | smu_i2c_probe(); |
| 1189 | #endif | 1472 | #endif |
| 1473 | |||
| 1474 | /* Now add plaform functions for some known devices */ | ||
| 1475 | pmac_i2c_devscan(pmac_i2c_dev_create); | ||
| 1476 | |||
| 1190 | return 0; | 1477 | return 0; |
| 1191 | } | 1478 | } |
| 1192 | arch_initcall(pmac_i2c_init); | 1479 | arch_initcall(pmac_i2c_init); |
| @@ -1216,6 +1503,9 @@ static int __init pmac_i2c_create_platform_devices(void) | |||
| 1216 | platform_device_add(bus->platform_dev); | 1503 | platform_device_add(bus->platform_dev); |
| 1217 | } | 1504 | } |
| 1218 | 1505 | ||
| 1506 | /* Now call platform "init" functions */ | ||
| 1507 | pmac_i2c_devscan(pmac_i2c_dev_init); | ||
| 1508 | |||
| 1219 | return 0; | 1509 | return 0; |
| 1220 | } | 1510 | } |
| 1221 | subsys_initcall(pmac_i2c_create_platform_devices); | 1511 | subsys_initcall(pmac_i2c_create_platform_devices); |
diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c new file mode 100644 index 000000000000..4ffd2a9832a0 --- /dev/null +++ b/arch/powerpc/platforms/powermac/pfunc_base.c | |||
| @@ -0,0 +1,405 @@ | |||
| 1 | #include <linux/config.h> | ||
| 2 | #include <linux/types.h> | ||
| 3 | #include <linux/init.h> | ||
| 4 | #include <linux/delay.h> | ||
| 5 | #include <linux/kernel.h> | ||
| 6 | #include <linux/interrupt.h> | ||
| 7 | #include <linux/spinlock.h> | ||
| 8 | |||
| 9 | #include <asm/pmac_feature.h> | ||
| 10 | #include <asm/pmac_pfunc.h> | ||
| 11 | |||
| 12 | #define DBG(fmt...) printk(fmt) | ||
| 13 | |||
| 14 | static irqreturn_t macio_gpio_irq(int irq, void *data, struct pt_regs *regs) | ||
| 15 | { | ||
| 16 | pmf_do_irq(data); | ||
| 17 | |||
| 18 | return IRQ_HANDLED; | ||
| 19 | } | ||
| 20 | |||
| 21 | static int macio_do_gpio_irq_enable(struct pmf_function *func) | ||
| 22 | { | ||
| 23 | if (func->node->n_intrs < 1) | ||
| 24 | return -EINVAL; | ||
| 25 | |||
| 26 | return request_irq(func->node->intrs[0].line, macio_gpio_irq, 0, | ||
| 27 | func->node->name, func); | ||
| 28 | } | ||
| 29 | |||
| 30 | static int macio_do_gpio_irq_disable(struct pmf_function *func) | ||
| 31 | { | ||
| 32 | if (func->node->n_intrs < 1) | ||
| 33 | return -EINVAL; | ||
| 34 | |||
| 35 | free_irq(func->node->intrs[0].line, func); | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static int macio_do_gpio_write(PMF_STD_ARGS, u8 value, u8 mask) | ||
| 40 | { | ||
| 41 | u8 __iomem *addr = (u8 __iomem *)func->driver_data; | ||
| 42 | unsigned long flags; | ||
| 43 | u8 tmp; | ||
| 44 | |||
| 45 | /* Check polarity */ | ||
| 46 | if (args && args->count && !args->u[0].v) | ||
| 47 | value = ~value; | ||
| 48 | |||
| 49 | /* Toggle the GPIO */ | ||
| 50 | spin_lock_irqsave(&feature_lock, flags); | ||
| 51 | tmp = readb(addr); | ||
| 52 | tmp = (tmp & ~mask) | (value & mask); | ||
| 53 | DBG("Do write 0x%02x to GPIO %s (%p)\n", | ||
| 54 | tmp, func->node->full_name, addr); | ||
| 55 | writeb(tmp, addr); | ||
| 56 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 57 | |||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static int macio_do_gpio_read(PMF_STD_ARGS, u8 mask, int rshift, u8 xor) | ||
| 62 | { | ||
| 63 | u8 __iomem *addr = (u8 __iomem *)func->driver_data; | ||
| 64 | u32 value; | ||
| 65 | |||
| 66 | /* Check if we have room for reply */ | ||
| 67 | if (args == NULL || args->count == 0 || args->u[0].p == NULL) | ||
| 68 | return -EINVAL; | ||
| 69 | |||
| 70 | value = readb(addr); | ||
| 71 | *args->u[0].p = ((value & mask) >> rshift) ^ xor; | ||
| 72 | |||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int macio_do_delay(PMF_STD_ARGS, u32 duration) | ||
| 77 | { | ||
| 78 | /* assume we can sleep ! */ | ||
| 79 | msleep((duration + 999) / 1000); | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static struct pmf_handlers macio_gpio_handlers = { | ||
| 84 | .irq_enable = macio_do_gpio_irq_enable, | ||
| 85 | .irq_disable = macio_do_gpio_irq_disable, | ||
| 86 | .write_gpio = macio_do_gpio_write, | ||
| 87 | .read_gpio = macio_do_gpio_read, | ||
| 88 | .delay = macio_do_delay, | ||
| 89 | }; | ||
| 90 | |||
| 91 | static void macio_gpio_init_one(struct macio_chip *macio) | ||
| 92 | { | ||
| 93 | struct device_node *gparent, *gp; | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Find the "gpio" parent node | ||
| 97 | */ | ||
| 98 | |||
| 99 | for (gparent = NULL; | ||
| 100 | (gparent = of_get_next_child(macio->of_node, gparent)) != NULL;) | ||
| 101 | if (strcmp(gparent->name, "gpio") == 0) | ||
| 102 | break; | ||
| 103 | if (gparent == NULL) | ||
| 104 | return; | ||
| 105 | |||
| 106 | DBG("Installing GPIO functions for macio %s\n", | ||
| 107 | macio->of_node->full_name); | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Ok, got one, we dont need anything special to track them down, so | ||
| 111 | * we just create them all | ||
| 112 | */ | ||
| 113 | for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) { | ||
| 114 | u32 *reg = (u32 *)get_property(gp, "reg", NULL); | ||
| 115 | unsigned long offset; | ||
| 116 | if (reg == NULL) | ||
| 117 | continue; | ||
| 118 | offset = *reg; | ||
| 119 | /* Deal with old style device-tree. We can safely hard code the | ||
| 120 | * offset for now too even if it's a bit gross ... | ||
| 121 | */ | ||
| 122 | if (offset < 0x50) | ||
| 123 | offset += 0x50; | ||
| 124 | offset += (unsigned long)macio->base; | ||
| 125 | pmf_register_driver(gp, &macio_gpio_handlers, (void *)offset); | ||
| 126 | } | ||
| 127 | |||
| 128 | DBG("Calling initial GPIO functions for macio %s\n", | ||
| 129 | macio->of_node->full_name); | ||
| 130 | |||
| 131 | /* And now we run all the init ones */ | ||
| 132 | for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) | ||
| 133 | pmf_do_functions(gp, NULL, 0, PMF_FLAGS_ON_INIT, NULL); | ||
| 134 | |||
| 135 | /* Note: We do not at this point implement the "at sleep" or "at wake" | ||
| 136 | * functions. I yet to find any for GPIOs anyway | ||
| 137 | */ | ||
| 138 | } | ||
| 139 | |||
| 140 | static int macio_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) | ||
| 141 | { | ||
| 142 | struct macio_chip *macio = func->driver_data; | ||
| 143 | unsigned long flags; | ||
| 144 | |||
| 145 | spin_lock_irqsave(&feature_lock, flags); | ||
| 146 | MACIO_OUT32(offset, (MACIO_IN32(offset) & ~mask) | (value & mask)); | ||
| 147 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int macio_do_read_reg32(PMF_STD_ARGS, u32 offset) | ||
| 152 | { | ||
| 153 | struct macio_chip *macio = func->driver_data; | ||
| 154 | |||
| 155 | /* Check if we have room for reply */ | ||
| 156 | if (args == NULL || args->count == 0 || args->u[0].p == NULL) | ||
| 157 | return -EINVAL; | ||
| 158 | |||
| 159 | *args->u[0].p = MACIO_IN32(offset); | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int macio_do_write_reg8(PMF_STD_ARGS, u32 offset, u8 value, u8 mask) | ||
| 164 | { | ||
| 165 | struct macio_chip *macio = func->driver_data; | ||
| 166 | unsigned long flags; | ||
| 167 | |||
| 168 | spin_lock_irqsave(&feature_lock, flags); | ||
| 169 | MACIO_OUT8(offset, (MACIO_IN8(offset) & ~mask) | (value & mask)); | ||
| 170 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | static int macio_do_read_reg8(PMF_STD_ARGS, u32 offset) | ||
| 175 | { | ||
| 176 | struct macio_chip *macio = func->driver_data; | ||
| 177 | |||
| 178 | /* Check if we have room for reply */ | ||
| 179 | if (args == NULL || args->count == 0 || args->u[0].p == NULL) | ||
| 180 | return -EINVAL; | ||
| 181 | |||
| 182 | *((u8 *)(args->u[0].p)) = MACIO_IN8(offset); | ||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | static int macio_do_read_reg32_msrx(PMF_STD_ARGS, u32 offset, u32 mask, | ||
| 187 | u32 shift, u32 xor) | ||
| 188 | { | ||
| 189 | struct macio_chip *macio = func->driver_data; | ||
| 190 | |||
| 191 | /* Check if we have room for reply */ | ||
| 192 | if (args == NULL || args->count == 0 || args->u[0].p == NULL) | ||
| 193 | return -EINVAL; | ||
| 194 | |||
| 195 | *args->u[0].p = ((MACIO_IN32(offset) & mask) >> shift) ^ xor; | ||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | static int macio_do_read_reg8_msrx(PMF_STD_ARGS, u32 offset, u32 mask, | ||
| 200 | u32 shift, u32 xor) | ||
| 201 | { | ||
| 202 | struct macio_chip *macio = func->driver_data; | ||
| 203 | |||
| 204 | /* Check if we have room for reply */ | ||
| 205 | if (args == NULL || args->count == 0 || args->u[0].p == NULL) | ||
| 206 | return -EINVAL; | ||
| 207 | |||
| 208 | *((u8 *)(args->u[0].p)) = ((MACIO_IN8(offset) & mask) >> shift) ^ xor; | ||
| 209 | return 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | static int macio_do_write_reg32_slm(PMF_STD_ARGS, u32 offset, u32 shift, | ||
| 213 | u32 mask) | ||
| 214 | { | ||
| 215 | struct macio_chip *macio = func->driver_data; | ||
| 216 | unsigned long flags; | ||
| 217 | u32 tmp, val; | ||
| 218 | |||
| 219 | /* Check args */ | ||
| 220 | if (args == NULL || args->count == 0) | ||
| 221 | return -EINVAL; | ||
| 222 | |||
| 223 | spin_lock_irqsave(&feature_lock, flags); | ||
| 224 | tmp = MACIO_IN32(offset); | ||
| 225 | val = args->u[0].v << shift; | ||
| 226 | tmp = (tmp & ~mask) | (val & mask); | ||
| 227 | MACIO_OUT32(offset, tmp); | ||
| 228 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static int macio_do_write_reg8_slm(PMF_STD_ARGS, u32 offset, u32 shift, | ||
| 233 | u32 mask) | ||
| 234 | { | ||
| 235 | struct macio_chip *macio = func->driver_data; | ||
| 236 | unsigned long flags; | ||
| 237 | u32 tmp, val; | ||
| 238 | |||
| 239 | /* Check args */ | ||
| 240 | if (args == NULL || args->count == 0) | ||
| 241 | return -EINVAL; | ||
| 242 | |||
| 243 | spin_lock_irqsave(&feature_lock, flags); | ||
| 244 | tmp = MACIO_IN8(offset); | ||
| 245 | val = args->u[0].v << shift; | ||
| 246 | tmp = (tmp & ~mask) | (val & mask); | ||
| 247 | MACIO_OUT8(offset, tmp); | ||
| 248 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct pmf_handlers macio_mmio_handlers = { | ||
| 253 | .write_reg32 = macio_do_write_reg32, | ||
| 254 | .read_reg32 = macio_do_read_reg32, | ||
| 255 | .write_reg8 = macio_do_write_reg8, | ||
| 256 | .read_reg32 = macio_do_read_reg8, | ||
| 257 | .read_reg32_msrx = macio_do_read_reg32_msrx, | ||
| 258 | .read_reg8_msrx = macio_do_read_reg8_msrx, | ||
| 259 | .write_reg32_slm = macio_do_write_reg32_slm, | ||
| 260 | .write_reg8_slm = macio_do_write_reg8_slm, | ||
| 261 | .delay = macio_do_delay, | ||
| 262 | }; | ||
| 263 | |||
| 264 | static void macio_mmio_init_one(struct macio_chip *macio) | ||
| 265 | { | ||
| 266 | DBG("Installing MMIO functions for macio %s\n", | ||
| 267 | macio->of_node->full_name); | ||
| 268 | |||
| 269 | pmf_register_driver(macio->of_node, &macio_mmio_handlers, macio); | ||
| 270 | } | ||
| 271 | |||
| 272 | static struct device_node *unin_hwclock; | ||
| 273 | |||
| 274 | static int unin_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) | ||
| 275 | { | ||
| 276 | unsigned long flags; | ||
| 277 | |||
| 278 | spin_lock_irqsave(&feature_lock, flags); | ||
| 279 | /* This is fairly bogus in darwin, but it should work for our needs | ||
| 280 | * implemeted that way: | ||
| 281 | */ | ||
| 282 | UN_OUT(offset, (UN_IN(offset) & ~mask) | (value & mask)); | ||
| 283 | spin_unlock_irqrestore(&feature_lock, flags); | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | |||
| 288 | static struct pmf_handlers unin_mmio_handlers = { | ||
| 289 | .write_reg32 = unin_do_write_reg32, | ||
| 290 | .delay = macio_do_delay, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static void uninorth_install_pfunc(void) | ||
| 294 | { | ||
| 295 | struct device_node *np; | ||
| 296 | |||
| 297 | DBG("Installing functions for UniN %s\n", | ||
| 298 | uninorth_node->full_name); | ||
| 299 | |||
| 300 | /* | ||
| 301 | * Install handlers for the bridge itself | ||
| 302 | */ | ||
| 303 | pmf_register_driver(uninorth_node, &unin_mmio_handlers, NULL); | ||
| 304 | pmf_do_functions(uninorth_node, NULL, 0, PMF_FLAGS_ON_INIT, NULL); | ||
| 305 | |||
| 306 | |||
| 307 | /* | ||
| 308 | * Install handlers for the hwclock child if any | ||
| 309 | */ | ||
| 310 | for (np = NULL; (np = of_get_next_child(uninorth_node, np)) != NULL;) | ||
| 311 | if (strcmp(np->name, "hw-clock") == 0) { | ||
| 312 | unin_hwclock = np; | ||
| 313 | break; | ||
| 314 | } | ||
| 315 | if (unin_hwclock) { | ||
| 316 | DBG("Installing functions for UniN clock %s\n", | ||
| 317 | unin_hwclock->full_name); | ||
| 318 | pmf_register_driver(unin_hwclock, &unin_mmio_handlers, NULL); | ||
| 319 | pmf_do_functions(unin_hwclock, NULL, 0, PMF_FLAGS_ON_INIT, | ||
| 320 | NULL); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | /* We export this as the SMP code might init us early */ | ||
| 325 | int __init pmac_pfunc_base_install(void) | ||
| 326 | { | ||
| 327 | static int pfbase_inited; | ||
| 328 | int i; | ||
| 329 | |||
| 330 | if (pfbase_inited) | ||
| 331 | return 0; | ||
| 332 | pfbase_inited = 1; | ||
| 333 | |||
| 334 | |||
| 335 | DBG("Installing base platform functions...\n"); | ||
| 336 | |||
| 337 | /* | ||
| 338 | * Locate mac-io chips and install handlers | ||
| 339 | */ | ||
| 340 | for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { | ||
| 341 | if (macio_chips[i].of_node) { | ||
| 342 | macio_mmio_init_one(&macio_chips[i]); | ||
| 343 | macio_gpio_init_one(&macio_chips[i]); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | /* | ||
| 348 | * Install handlers for northbridge and direct mapped hwclock | ||
| 349 | * if any. We do not implement the config space access callback | ||
| 350 | * which is only ever used for functions that we do not call in | ||
| 351 | * the current driver (enabling/disabling cells in U2, mostly used | ||
| 352 | * to restore the PCI settings, we do that differently) | ||
| 353 | */ | ||
| 354 | if (uninorth_node && uninorth_base) | ||
| 355 | uninorth_install_pfunc(); | ||
| 356 | |||
| 357 | DBG("All base functions installed\n"); | ||
| 358 | |||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | |||
| 362 | arch_initcall(pmac_pfunc_base_install); | ||
| 363 | |||
| 364 | #ifdef CONFIG_PM | ||
| 365 | |||
| 366 | /* Those can be called by pmac_feature. Ultimately, I should use a sysdev | ||
| 367 | * or a device, but for now, that's good enough until I sort out some | ||
| 368 | * ordering issues. Also, we do not bother with GPIOs, as so far I yet have | ||
| 369 | * to see a case where a GPIO function has the on-suspend or on-resume bit | ||
| 370 | */ | ||
| 371 | void pmac_pfunc_base_suspend(void) | ||
| 372 | { | ||
| 373 | int i; | ||
| 374 | |||
| 375 | for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { | ||
| 376 | if (macio_chips[i].of_node) | ||
| 377 | pmf_do_functions(macio_chips[i].of_node, NULL, 0, | ||
| 378 | PMF_FLAGS_ON_SLEEP, NULL); | ||
| 379 | } | ||
| 380 | if (uninorth_node) | ||
| 381 | pmf_do_functions(uninorth_node, NULL, 0, | ||
| 382 | PMF_FLAGS_ON_SLEEP, NULL); | ||
| 383 | if (unin_hwclock) | ||
| 384 | pmf_do_functions(unin_hwclock, NULL, 0, | ||
| 385 | PMF_FLAGS_ON_SLEEP, NULL); | ||
| 386 | } | ||
| 387 | |||
| 388 | void pmac_pfunc_base_resume(void) | ||
| 389 | { | ||
| 390 | int i; | ||
| 391 | |||
| 392 | if (unin_hwclock) | ||
| 393 | pmf_do_functions(unin_hwclock, NULL, 0, | ||
| 394 | PMF_FLAGS_ON_WAKE, NULL); | ||
| 395 | if (uninorth_node) | ||
| 396 | pmf_do_functions(uninorth_node, NULL, 0, | ||
| 397 | PMF_FLAGS_ON_WAKE, NULL); | ||
| 398 | for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { | ||
| 399 | if (macio_chips[i].of_node) | ||
| 400 | pmf_do_functions(macio_chips[i].of_node, NULL, 0, | ||
| 401 | PMF_FLAGS_ON_WAKE, NULL); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | #endif /* CONFIG_PM */ | ||
diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c new file mode 100644 index 000000000000..c32c623001dc --- /dev/null +++ b/arch/powerpc/platforms/powermac/pfunc_core.c | |||
| @@ -0,0 +1,989 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * FIXME: Properly make this race free with refcounting etc... | ||
| 4 | * | ||
| 5 | * FIXME: LOCKING !!! | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/config.h> | ||
| 9 | #include <linux/init.h> | ||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/spinlock.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | |||
| 15 | #include <asm/semaphore.h> | ||
| 16 | #include <asm/prom.h> | ||
| 17 | #include <asm/pmac_pfunc.h> | ||
| 18 | |||
| 19 | /* Debug */ | ||
| 20 | #define LOG_PARSE(fmt...) | ||
| 21 | #define LOG_ERROR(fmt...) printk(fmt) | ||
| 22 | #define LOG_BLOB(t,b,c) | ||
| 23 | #define DBG(fmt...) printk(fmt) | ||
| 24 | |||
| 25 | /* Command numbers */ | ||
| 26 | #define PMF_CMD_LIST 0 | ||
| 27 | #define PMF_CMD_WRITE_GPIO 1 | ||
| 28 | #define PMF_CMD_READ_GPIO 2 | ||
| 29 | #define PMF_CMD_WRITE_REG32 3 | ||
| 30 | #define PMF_CMD_READ_REG32 4 | ||
| 31 | #define PMF_CMD_WRITE_REG16 5 | ||
| 32 | #define PMF_CMD_READ_REG16 6 | ||
| 33 | #define PMF_CMD_WRITE_REG8 7 | ||
| 34 | #define PMF_CMD_READ_REG8 8 | ||
| 35 | #define PMF_CMD_DELAY 9 | ||
| 36 | #define PMF_CMD_WAIT_REG32 10 | ||
| 37 | #define PMF_CMD_WAIT_REG16 11 | ||
| 38 | #define PMF_CMD_WAIT_REG8 12 | ||
| 39 | #define PMF_CMD_READ_I2C 13 | ||
| 40 | #define PMF_CMD_WRITE_I2C 14 | ||
| 41 | #define PMF_CMD_RMW_I2C 15 | ||
| 42 | #define PMF_CMD_GEN_I2C 16 | ||
| 43 | #define PMF_CMD_SHIFT_BYTES_RIGHT 17 | ||
| 44 | #define PMF_CMD_SHIFT_BYTES_LEFT 18 | ||
| 45 | #define PMF_CMD_READ_CFG 19 | ||
| 46 | #define PMF_CMD_WRITE_CFG 20 | ||
| 47 | #define PMF_CMD_RMW_CFG 21 | ||
| 48 | #define PMF_CMD_READ_I2C_SUBADDR 22 | ||
| 49 | #define PMF_CMD_WRITE_I2C_SUBADDR 23 | ||
| 50 | #define PMF_CMD_SET_I2C_MODE 24 | ||
| 51 | #define PMF_CMD_RMW_I2C_SUBADDR 25 | ||
| 52 | #define PMF_CMD_READ_REG32_MASK_SHR_XOR 26 | ||
| 53 | #define PMF_CMD_READ_REG16_MASK_SHR_XOR 27 | ||
| 54 | #define PMF_CMD_READ_REG8_MASK_SHR_XOR 28 | ||
| 55 | #define PMF_CMD_WRITE_REG32_SHL_MASK 29 | ||
| 56 | #define PMF_CMD_WRITE_REG16_SHL_MASK 30 | ||
| 57 | #define PMF_CMD_WRITE_REG8_SHL_MASK 31 | ||
| 58 | #define PMF_CMD_MASK_AND_COMPARE 32 | ||
| 59 | #define PMF_CMD_COUNT 33 | ||
| 60 | |||
| 61 | /* This structure holds the state of the parser while walking through | ||
| 62 | * a function definition | ||
| 63 | */ | ||
| 64 | struct pmf_cmd { | ||
| 65 | const void *cmdptr; | ||
| 66 | const void *cmdend; | ||
| 67 | struct pmf_function *func; | ||
| 68 | void *instdata; | ||
| 69 | struct pmf_args *args; | ||
| 70 | int error; | ||
| 71 | }; | ||
| 72 | |||
| 73 | #if 0 | ||
| 74 | /* Debug output */ | ||
| 75 | static void print_blob(const char *title, const void *blob, int bytes) | ||
| 76 | { | ||
| 77 | printk("%s", title); | ||
| 78 | while(bytes--) { | ||
| 79 | printk("%02x ", *((u8 *)blob)); | ||
| 80 | blob += 1; | ||
| 81 | } | ||
| 82 | printk("\n"); | ||
| 83 | } | ||
| 84 | #endif | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Parser helpers | ||
| 88 | */ | ||
| 89 | |||
| 90 | static u32 pmf_next32(struct pmf_cmd *cmd) | ||
| 91 | { | ||
| 92 | u32 value; | ||
| 93 | if ((cmd->cmdend - cmd->cmdptr) < 4) { | ||
| 94 | cmd->error = 1; | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | value = *((u32 *)cmd->cmdptr); | ||
| 98 | cmd->cmdptr += 4; | ||
| 99 | return value; | ||
| 100 | } | ||
| 101 | |||
| 102 | static const void* pmf_next_blob(struct pmf_cmd *cmd, int count) | ||
| 103 | { | ||
| 104 | const void *value; | ||
| 105 | if ((cmd->cmdend - cmd->cmdptr) < count) { | ||
| 106 | cmd->error = 1; | ||
| 107 | return NULL; | ||
| 108 | } | ||
| 109 | value = cmd->cmdptr; | ||
| 110 | cmd->cmdptr += count; | ||
| 111 | return value; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Individual command parsers | ||
| 116 | */ | ||
| 117 | |||
| 118 | #define PMF_PARSE_CALL(name, cmd, handlers, p...) \ | ||
| 119 | do { \ | ||
| 120 | if (cmd->error) \ | ||
| 121 | return -ENXIO; \ | ||
| 122 | if (handlers == NULL) \ | ||
| 123 | return 0; \ | ||
| 124 | if (handlers->name) \ | ||
| 125 | return handlers->name(cmd->func, cmd->instdata, \ | ||
| 126 | cmd->args, p); \ | ||
| 127 | return -1; \ | ||
| 128 | } while(0) \ | ||
| 129 | |||
| 130 | |||
| 131 | static int pmf_parser_write_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 132 | { | ||
| 133 | u8 value = (u8)pmf_next32(cmd); | ||
| 134 | u8 mask = (u8)pmf_next32(cmd); | ||
| 135 | |||
| 136 | LOG_PARSE("pmf: write_gpio(value: %02x, mask: %02x)\n", value, mask); | ||
| 137 | |||
| 138 | PMF_PARSE_CALL(write_gpio, cmd, h, value, mask); | ||
| 139 | } | ||
| 140 | |||
| 141 | static int pmf_parser_read_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 142 | { | ||
| 143 | u8 mask = (u8)pmf_next32(cmd); | ||
| 144 | int rshift = (int)pmf_next32(cmd); | ||
| 145 | u8 xor = (u8)pmf_next32(cmd); | ||
| 146 | |||
| 147 | LOG_PARSE("pmf: read_gpio(mask: %02x, rshift: %d, xor: %02x)\n", | ||
| 148 | mask, rshift, xor); | ||
| 149 | |||
| 150 | PMF_PARSE_CALL(read_gpio, cmd, h, mask, rshift, xor); | ||
| 151 | } | ||
| 152 | |||
| 153 | static int pmf_parser_write_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 154 | { | ||
| 155 | u32 offset = pmf_next32(cmd); | ||
| 156 | u32 value = pmf_next32(cmd); | ||
| 157 | u32 mask = pmf_next32(cmd); | ||
| 158 | |||
| 159 | LOG_PARSE("pmf: write_reg32(offset: %08x, value: %08x, mask: %08x)\n", | ||
| 160 | offset, value, mask); | ||
| 161 | |||
| 162 | PMF_PARSE_CALL(write_reg32, cmd, h, offset, value, mask); | ||
| 163 | } | ||
| 164 | |||
| 165 | static int pmf_parser_read_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 166 | { | ||
| 167 | u32 offset = pmf_next32(cmd); | ||
| 168 | |||
| 169 | LOG_PARSE("pmf: read_reg32(offset: %08x)\n", offset); | ||
| 170 | |||
| 171 | PMF_PARSE_CALL(read_reg32, cmd, h, offset); | ||
| 172 | } | ||
| 173 | |||
| 174 | |||
| 175 | static int pmf_parser_write_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 176 | { | ||
| 177 | u32 offset = pmf_next32(cmd); | ||
| 178 | u16 value = (u16)pmf_next32(cmd); | ||
| 179 | u16 mask = (u16)pmf_next32(cmd); | ||
| 180 | |||
| 181 | LOG_PARSE("pmf: write_reg16(offset: %08x, value: %04x, mask: %04x)\n", | ||
| 182 | offset, value, mask); | ||
| 183 | |||
| 184 | PMF_PARSE_CALL(write_reg16, cmd, h, offset, value, mask); | ||
| 185 | } | ||
| 186 | |||
| 187 | static int pmf_parser_read_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 188 | { | ||
| 189 | u32 offset = pmf_next32(cmd); | ||
| 190 | |||
| 191 | LOG_PARSE("pmf: read_reg16(offset: %08x)\n", offset); | ||
| 192 | |||
| 193 | PMF_PARSE_CALL(read_reg16, cmd, h, offset); | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | static int pmf_parser_write_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 198 | { | ||
| 199 | u32 offset = pmf_next32(cmd); | ||
| 200 | u8 value = (u16)pmf_next32(cmd); | ||
| 201 | u8 mask = (u16)pmf_next32(cmd); | ||
| 202 | |||
| 203 | LOG_PARSE("pmf: write_reg8(offset: %08x, value: %02x, mask: %02x)\n", | ||
| 204 | offset, value, mask); | ||
| 205 | |||
| 206 | PMF_PARSE_CALL(write_reg8, cmd, h, offset, value, mask); | ||
| 207 | } | ||
| 208 | |||
| 209 | static int pmf_parser_read_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 210 | { | ||
| 211 | u32 offset = pmf_next32(cmd); | ||
| 212 | |||
| 213 | LOG_PARSE("pmf: read_reg8(offset: %08x)\n", offset); | ||
| 214 | |||
| 215 | PMF_PARSE_CALL(read_reg8, cmd, h, offset); | ||
| 216 | } | ||
| 217 | |||
| 218 | static int pmf_parser_delay(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 219 | { | ||
| 220 | u32 duration = pmf_next32(cmd); | ||
| 221 | |||
| 222 | LOG_PARSE("pmf: delay(duration: %d us)\n", duration); | ||
| 223 | |||
| 224 | PMF_PARSE_CALL(delay, cmd, h, duration); | ||
| 225 | } | ||
| 226 | |||
| 227 | static int pmf_parser_wait_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 228 | { | ||
| 229 | u32 offset = pmf_next32(cmd); | ||
| 230 | u32 value = pmf_next32(cmd); | ||
| 231 | u32 mask = pmf_next32(cmd); | ||
| 232 | |||
| 233 | LOG_PARSE("pmf: wait_reg32(offset: %08x, comp_value: %08x,mask: %08x)\n", | ||
| 234 | offset, value, mask); | ||
| 235 | |||
| 236 | PMF_PARSE_CALL(wait_reg32, cmd, h, offset, value, mask); | ||
| 237 | } | ||
| 238 | |||
| 239 | static int pmf_parser_wait_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 240 | { | ||
| 241 | u32 offset = pmf_next32(cmd); | ||
| 242 | u16 value = (u16)pmf_next32(cmd); | ||
| 243 | u16 mask = (u16)pmf_next32(cmd); | ||
| 244 | |||
| 245 | LOG_PARSE("pmf: wait_reg16(offset: %08x, comp_value: %04x,mask: %04x)\n", | ||
| 246 | offset, value, mask); | ||
| 247 | |||
| 248 | PMF_PARSE_CALL(wait_reg16, cmd, h, offset, value, mask); | ||
| 249 | } | ||
| 250 | |||
| 251 | static int pmf_parser_wait_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 252 | { | ||
| 253 | u32 offset = pmf_next32(cmd); | ||
| 254 | u8 value = (u8)pmf_next32(cmd); | ||
| 255 | u8 mask = (u8)pmf_next32(cmd); | ||
| 256 | |||
| 257 | LOG_PARSE("pmf: wait_reg8(offset: %08x, comp_value: %02x,mask: %02x)\n", | ||
| 258 | offset, value, mask); | ||
| 259 | |||
| 260 | PMF_PARSE_CALL(wait_reg8, cmd, h, offset, value, mask); | ||
| 261 | } | ||
| 262 | |||
| 263 | static int pmf_parser_read_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 264 | { | ||
| 265 | u32 bytes = pmf_next32(cmd); | ||
| 266 | |||
| 267 | LOG_PARSE("pmf: read_i2c(bytes: %ud)\n", bytes); | ||
| 268 | |||
| 269 | PMF_PARSE_CALL(read_i2c, cmd, h, bytes); | ||
| 270 | } | ||
| 271 | |||
| 272 | static int pmf_parser_write_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 273 | { | ||
| 274 | u32 bytes = pmf_next32(cmd); | ||
| 275 | const void *blob = pmf_next_blob(cmd, bytes); | ||
| 276 | |||
| 277 | LOG_PARSE("pmf: write_i2c(bytes: %ud) ...\n", bytes); | ||
| 278 | LOG_BLOB("pmf: data: \n", blob, bytes); | ||
| 279 | |||
| 280 | PMF_PARSE_CALL(write_i2c, cmd, h, bytes, blob); | ||
| 281 | } | ||
| 282 | |||
| 283 | |||
| 284 | static int pmf_parser_rmw_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 285 | { | ||
| 286 | u32 maskbytes = pmf_next32(cmd); | ||
| 287 | u32 valuesbytes = pmf_next32(cmd); | ||
| 288 | u32 totalbytes = pmf_next32(cmd); | ||
| 289 | const void *maskblob = pmf_next_blob(cmd, maskbytes); | ||
| 290 | const void *valuesblob = pmf_next_blob(cmd, valuesbytes); | ||
| 291 | |||
| 292 | LOG_PARSE("pmf: rmw_i2c(maskbytes: %ud, valuebytes: %ud, " | ||
| 293 | "totalbytes: %d) ...\n", | ||
| 294 | maskbytes, valuesbytes, totalbytes); | ||
| 295 | LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); | ||
| 296 | LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); | ||
| 297 | |||
| 298 | PMF_PARSE_CALL(rmw_i2c, cmd, h, maskbytes, valuesbytes, totalbytes, | ||
| 299 | maskblob, valuesblob); | ||
| 300 | } | ||
| 301 | |||
| 302 | static int pmf_parser_read_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 303 | { | ||
| 304 | u32 offset = pmf_next32(cmd); | ||
| 305 | u32 bytes = pmf_next32(cmd); | ||
| 306 | |||
| 307 | LOG_PARSE("pmf: read_cfg(offset: %x, bytes: %ud)\n", offset, bytes); | ||
| 308 | |||
| 309 | PMF_PARSE_CALL(read_cfg, cmd, h, offset, bytes); | ||
| 310 | } | ||
| 311 | |||
| 312 | |||
| 313 | static int pmf_parser_write_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 314 | { | ||
| 315 | u32 offset = pmf_next32(cmd); | ||
| 316 | u32 bytes = pmf_next32(cmd); | ||
| 317 | const void *blob = pmf_next_blob(cmd, bytes); | ||
| 318 | |||
| 319 | LOG_PARSE("pmf: write_cfg(offset: %x, bytes: %ud)\n", offset, bytes); | ||
| 320 | LOG_BLOB("pmf: data: \n", blob, bytes); | ||
| 321 | |||
| 322 | PMF_PARSE_CALL(write_cfg, cmd, h, offset, bytes, blob); | ||
| 323 | } | ||
| 324 | |||
| 325 | static int pmf_parser_rmw_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 326 | { | ||
| 327 | u32 offset = pmf_next32(cmd); | ||
| 328 | u32 maskbytes = pmf_next32(cmd); | ||
| 329 | u32 valuesbytes = pmf_next32(cmd); | ||
| 330 | u32 totalbytes = pmf_next32(cmd); | ||
| 331 | const void *maskblob = pmf_next_blob(cmd, maskbytes); | ||
| 332 | const void *valuesblob = pmf_next_blob(cmd, valuesbytes); | ||
| 333 | |||
| 334 | LOG_PARSE("pmf: rmw_cfg(maskbytes: %ud, valuebytes: %ud," | ||
| 335 | " totalbytes: %d) ...\n", | ||
| 336 | maskbytes, valuesbytes, totalbytes); | ||
| 337 | LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); | ||
| 338 | LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); | ||
| 339 | |||
| 340 | PMF_PARSE_CALL(rmw_cfg, cmd, h, offset, maskbytes, valuesbytes, | ||
| 341 | totalbytes, maskblob, valuesblob); | ||
| 342 | } | ||
| 343 | |||
| 344 | |||
| 345 | static int pmf_parser_read_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 346 | { | ||
| 347 | u8 subaddr = (u8)pmf_next32(cmd); | ||
| 348 | u32 bytes = pmf_next32(cmd); | ||
| 349 | |||
| 350 | LOG_PARSE("pmf: read_i2c_sub(subaddr: %x, bytes: %ud)\n", | ||
| 351 | subaddr, bytes); | ||
| 352 | |||
| 353 | PMF_PARSE_CALL(read_i2c_sub, cmd, h, subaddr, bytes); | ||
| 354 | } | ||
| 355 | |||
| 356 | static int pmf_parser_write_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 357 | { | ||
| 358 | u8 subaddr = (u8)pmf_next32(cmd); | ||
| 359 | u32 bytes = pmf_next32(cmd); | ||
| 360 | const void *blob = pmf_next_blob(cmd, bytes); | ||
| 361 | |||
| 362 | LOG_PARSE("pmf: write_i2c_sub(subaddr: %x, bytes: %ud) ...\n", | ||
| 363 | subaddr, bytes); | ||
| 364 | LOG_BLOB("pmf: data: \n", blob, bytes); | ||
| 365 | |||
| 366 | PMF_PARSE_CALL(write_i2c_sub, cmd, h, subaddr, bytes, blob); | ||
| 367 | } | ||
| 368 | |||
| 369 | static int pmf_parser_set_i2c_mode(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 370 | { | ||
| 371 | u32 mode = pmf_next32(cmd); | ||
| 372 | |||
| 373 | LOG_PARSE("pmf: set_i2c_mode(mode: %d)\n", mode); | ||
| 374 | |||
| 375 | PMF_PARSE_CALL(set_i2c_mode, cmd, h, mode); | ||
| 376 | } | ||
| 377 | |||
| 378 | |||
| 379 | static int pmf_parser_rmw_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) | ||
| 380 | { | ||
| 381 | u8 subaddr = (u8)pmf_next32(cmd); | ||
| 382 | u32 maskbytes = pmf_next32(cmd); | ||
| 383 | u32 valuesbytes = pmf_next32(cmd); | ||
| 384 | u32 totalbytes = pmf_next32(cmd); | ||
| 385 | const void *maskblob = pmf_next_blob(cmd, maskbytes); | ||
| 386 | const void *valuesblob = pmf_next_blob(cmd, valuesbytes); | ||
| 387 | |||
| 388 | LOG_PARSE("pmf: rmw_i2c_sub(subaddr: %x, maskbytes: %ud, valuebytes: %ud" | ||
| 389 | ", totalbytes: %d) ...\n", | ||
| 390 | subaddr, maskbytes, valuesbytes, totalbytes); | ||
| 391 | LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); | ||
| 392 | LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); | ||
| 393 | |||
| 394 | PMF_PARSE_CALL(rmw_i2c_sub, cmd, h, subaddr, maskbytes, valuesbytes, | ||
| 395 | totalbytes, maskblob, valuesblob); | ||
| 396 | } | ||
| 397 | |||
| 398 | static int pmf_parser_read_reg32_msrx(struct pmf_cmd *cmd, | ||
| 399 | struct pmf_handlers *h) | ||
| 400 | { | ||
| 401 | u32 offset = pmf_next32(cmd); | ||
| 402 | u32 mask = pmf_next32(cmd); | ||
| 403 | u32 shift = pmf_next32(cmd); | ||
| 404 | u32 xor = pmf_next32(cmd); | ||
| 405 | |||
| 406 | LOG_PARSE("pmf: read_reg32_msrx(offset: %x, mask: %x, shift: %x," | ||
| 407 | " xor: %x\n", offset, mask, shift, xor); | ||
| 408 | |||
| 409 | PMF_PARSE_CALL(read_reg32_msrx, cmd, h, offset, mask, shift, xor); | ||
| 410 | } | ||
| 411 | |||
| 412 | static int pmf_parser_read_reg16_msrx(struct pmf_cmd *cmd, | ||
| 413 | struct pmf_handlers *h) | ||
| 414 | { | ||
| 415 | u32 offset = pmf_next32(cmd); | ||
| 416 | u32 mask = pmf_next32(cmd); | ||
| 417 | u32 shift = pmf_next32(cmd); | ||
| 418 | u32 xor = pmf_next32(cmd); | ||
| 419 | |||
| 420 | LOG_PARSE("pmf: read_reg16_msrx(offset: %x, mask: %x, shift: %x," | ||
| 421 | " xor: %x\n", offset, mask, shift, xor); | ||
| 422 | |||
| 423 | PMF_PARSE_CALL(read_reg16_msrx, cmd, h, offset, mask, shift, xor); | ||
| 424 | } | ||
| 425 | static int pmf_parser_read_reg8_msrx(struct pmf_cmd *cmd, | ||
| 426 | struct pmf_handlers *h) | ||
| 427 | { | ||
| 428 | u32 offset = pmf_next32(cmd); | ||
| 429 | u32 mask = pmf_next32(cmd); | ||
| 430 | u32 shift = pmf_next32(cmd); | ||
| 431 | u32 xor = pmf_next32(cmd); | ||
| 432 | |||
| 433 | LOG_PARSE("pmf: read_reg8_msrx(offset: %x, mask: %x, shift: %x," | ||
| 434 | " xor: %x\n", offset, mask, shift, xor); | ||
| 435 | |||
| 436 | PMF_PARSE_CALL(read_reg8_msrx, cmd, h, offset, mask, shift, xor); | ||
| 437 | } | ||
| 438 | |||
| 439 | static int pmf_parser_write_reg32_slm(struct pmf_cmd *cmd, | ||
| 440 | struct pmf_handlers *h) | ||
| 441 | { | ||
| 442 | u32 offset = pmf_next32(cmd); | ||
| 443 | u32 shift = pmf_next32(cmd); | ||
| 444 | u32 mask = pmf_next32(cmd); | ||
| 445 | |||
| 446 | LOG_PARSE("pmf: write_reg32_slm(offset: %x, shift: %x, mask: %x\n", | ||
| 447 | offset, shift, mask); | ||
| 448 | |||
| 449 | PMF_PARSE_CALL(write_reg32_slm, cmd, h, offset, shift, mask); | ||
| 450 | } | ||
| 451 | |||
| 452 | static int pmf_parser_write_reg16_slm(struct pmf_cmd *cmd, | ||
| 453 | struct pmf_handlers *h) | ||
| 454 | { | ||
| 455 | u32 offset = pmf_next32(cmd); | ||
| 456 | u32 shift = pmf_next32(cmd); | ||
| 457 | u32 mask = pmf_next32(cmd); | ||
| 458 | |||
| 459 | LOG_PARSE("pmf: write_reg16_slm(offset: %x, shift: %x, mask: %x\n", | ||
| 460 | offset, shift, mask); | ||
| 461 | |||
| 462 | PMF_PARSE_CALL(write_reg16_slm, cmd, h, offset, shift, mask); | ||
| 463 | } | ||
| 464 | |||
| 465 | static int pmf_parser_write_reg8_slm(struct pmf_cmd *cmd, | ||
| 466 | struct pmf_handlers *h) | ||
| 467 | { | ||
| 468 | u32 offset = pmf_next32(cmd); | ||
| 469 | u32 shift = pmf_next32(cmd); | ||
| 470 | u32 mask = pmf_next32(cmd); | ||
| 471 | |||
| 472 | LOG_PARSE("pmf: write_reg8_slm(offset: %x, shift: %x, mask: %x\n", | ||
| 473 | offset, shift, mask); | ||
| 474 | |||
| 475 | PMF_PARSE_CALL(write_reg8_slm, cmd, h, offset, shift, mask); | ||
| 476 | } | ||
| 477 | |||
| 478 | static int pmf_parser_mask_and_compare(struct pmf_cmd *cmd, | ||
| 479 | struct pmf_handlers *h) | ||
| 480 | { | ||
| 481 | u32 bytes = pmf_next32(cmd); | ||
| 482 | const void *maskblob = pmf_next_blob(cmd, bytes); | ||
| 483 | const void *valuesblob = pmf_next_blob(cmd, bytes); | ||
| 484 | |||
| 485 | LOG_PARSE("pmf: mask_and_compare(length: %ud ...\n", bytes); | ||
| 486 | LOG_BLOB("pmf: mask data: \n", maskblob, bytes); | ||
| 487 | LOG_BLOB("pmf: values data: \n", valuesblob, bytes); | ||
| 488 | |||
| 489 | PMF_PARSE_CALL(mask_and_compare, cmd, h, | ||
| 490 | bytes, maskblob, valuesblob); | ||
| 491 | } | ||
| 492 | |||
| 493 | |||
| 494 | typedef int (*pmf_cmd_parser_t)(struct pmf_cmd *cmd, struct pmf_handlers *h); | ||
| 495 | |||
| 496 | static pmf_cmd_parser_t pmf_parsers[PMF_CMD_COUNT] = | ||
| 497 | { | ||
| 498 | NULL, | ||
| 499 | pmf_parser_write_gpio, | ||
| 500 | pmf_parser_read_gpio, | ||
| 501 | pmf_parser_write_reg32, | ||
| 502 | pmf_parser_read_reg32, | ||
| 503 | pmf_parser_write_reg16, | ||
| 504 | pmf_parser_read_reg16, | ||
| 505 | pmf_parser_write_reg8, | ||
| 506 | pmf_parser_read_reg8, | ||
| 507 | pmf_parser_delay, | ||
| 508 | pmf_parser_wait_reg32, | ||
| 509 | pmf_parser_wait_reg16, | ||
| 510 | pmf_parser_wait_reg8, | ||
| 511 | pmf_parser_read_i2c, | ||
| 512 | pmf_parser_write_i2c, | ||
| 513 | pmf_parser_rmw_i2c, | ||
| 514 | NULL, /* Bogus command */ | ||
| 515 | NULL, /* Shift bytes right: NYI */ | ||
| 516 | NULL, /* Shift bytes left: NYI */ | ||
| 517 | pmf_parser_read_cfg, | ||
| 518 | pmf_parser_write_cfg, | ||
| 519 | pmf_parser_rmw_cfg, | ||
| 520 | pmf_parser_read_i2c_sub, | ||
| 521 | pmf_parser_write_i2c_sub, | ||
| 522 | pmf_parser_set_i2c_mode, | ||
| 523 | pmf_parser_rmw_i2c_sub, | ||
| 524 | pmf_parser_read_reg32_msrx, | ||
| 525 | pmf_parser_read_reg16_msrx, | ||
| 526 | pmf_parser_read_reg8_msrx, | ||
| 527 | pmf_parser_write_reg32_slm, | ||
| 528 | pmf_parser_write_reg16_slm, | ||
| 529 | pmf_parser_write_reg8_slm, | ||
| 530 | pmf_parser_mask_and_compare, | ||
| 531 | }; | ||
| 532 | |||
| 533 | struct pmf_device { | ||
| 534 | struct list_head link; | ||
| 535 | struct device_node *node; | ||
| 536 | struct pmf_handlers *handlers; | ||
| 537 | struct list_head functions; | ||
| 538 | struct kref ref; | ||
| 539 | }; | ||
| 540 | |||
| 541 | static LIST_HEAD(pmf_devices); | ||
| 542 | static spinlock_t pmf_lock = SPIN_LOCK_UNLOCKED; | ||
| 543 | |||
| 544 | static void pmf_release_device(struct kref *kref) | ||
| 545 | { | ||
| 546 | struct pmf_device *dev = container_of(kref, struct pmf_device, ref); | ||
| 547 | kfree(dev); | ||
| 548 | } | ||
| 549 | |||
| 550 | static inline void pmf_put_device(struct pmf_device *dev) | ||
| 551 | { | ||
| 552 | kref_put(&dev->ref, pmf_release_device); | ||
| 553 | } | ||
| 554 | |||
| 555 | static inline struct pmf_device *pmf_get_device(struct pmf_device *dev) | ||
| 556 | { | ||
| 557 | kref_get(&dev->ref); | ||
| 558 | return dev; | ||
| 559 | } | ||
| 560 | |||
| 561 | static inline struct pmf_device *pmf_find_device(struct device_node *np) | ||
| 562 | { | ||
| 563 | struct pmf_device *dev; | ||
| 564 | |||
| 565 | list_for_each_entry(dev, &pmf_devices, link) { | ||
| 566 | if (dev->node == np) | ||
| 567 | return pmf_get_device(dev); | ||
| 568 | } | ||
| 569 | return NULL; | ||
| 570 | } | ||
| 571 | |||
| 572 | static int pmf_parse_one(struct pmf_function *func, | ||
| 573 | struct pmf_handlers *handlers, | ||
| 574 | void *instdata, struct pmf_args *args) | ||
| 575 | { | ||
| 576 | struct pmf_cmd cmd; | ||
| 577 | u32 ccode; | ||
| 578 | int count, rc; | ||
| 579 | |||
| 580 | cmd.cmdptr = func->data; | ||
| 581 | cmd.cmdend = func->data + func->length; | ||
| 582 | cmd.func = func; | ||
| 583 | cmd.instdata = instdata; | ||
| 584 | cmd.args = args; | ||
| 585 | cmd.error = 0; | ||
| 586 | |||
| 587 | LOG_PARSE("pmf: func %s, %d bytes, %s...\n", | ||
| 588 | func->name, func->length, | ||
| 589 | handlers ? "executing" : "parsing"); | ||
| 590 | |||
| 591 | /* One subcommand to parse for now */ | ||
| 592 | count = 1; | ||
| 593 | |||
| 594 | while(count-- && cmd.cmdptr < cmd.cmdend) { | ||
| 595 | /* Get opcode */ | ||
| 596 | ccode = pmf_next32(&cmd); | ||
| 597 | /* Check if we are hitting a command list, fetch new count */ | ||
| 598 | if (ccode == 0) { | ||
| 599 | count = pmf_next32(&cmd) - 1; | ||
| 600 | ccode = pmf_next32(&cmd); | ||
| 601 | } | ||
| 602 | if (cmd.error) { | ||
| 603 | LOG_ERROR("pmf: parse error, not enough data\n"); | ||
| 604 | return -ENXIO; | ||
| 605 | } | ||
| 606 | if (ccode >= PMF_CMD_COUNT) { | ||
| 607 | LOG_ERROR("pmf: command code %d unknown !\n", ccode); | ||
| 608 | return -ENXIO; | ||
| 609 | } | ||
| 610 | if (pmf_parsers[ccode] == NULL) { | ||
| 611 | LOG_ERROR("pmf: no parser for command %d !\n", ccode); | ||
| 612 | return -ENXIO; | ||
| 613 | } | ||
| 614 | rc = pmf_parsers[ccode](&cmd, handlers); | ||
| 615 | if (rc != 0) { | ||
| 616 | LOG_ERROR("pmf: parser for command %d returned" | ||
| 617 | " error %d\n", ccode, rc); | ||
| 618 | return rc; | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | /* We are doing an initial parse pass, we need to adjust the size */ | ||
| 623 | if (handlers == NULL) | ||
| 624 | func->length = cmd.cmdptr - func->data; | ||
| 625 | |||
| 626 | return 0; | ||
| 627 | } | ||
| 628 | |||
| 629 | static int pmf_add_function_prop(struct pmf_device *dev, void *driverdata, | ||
| 630 | const char *name, u32 *data, | ||
| 631 | unsigned int length) | ||
| 632 | { | ||
| 633 | int count = 0; | ||
| 634 | struct pmf_function *func = NULL; | ||
| 635 | |||
| 636 | DBG("pmf: Adding functions for platform-do-%s\n", name); | ||
| 637 | |||
| 638 | while (length >= 12) { | ||
| 639 | /* Allocate a structure */ | ||
| 640 | func = kzalloc(sizeof(struct pmf_function), GFP_KERNEL); | ||
| 641 | if (func == NULL) | ||
| 642 | goto bail; | ||
| 643 | kref_init(&func->ref); | ||
| 644 | INIT_LIST_HEAD(&func->irq_clients); | ||
| 645 | func->node = dev->node; | ||
| 646 | func->driver_data = driverdata; | ||
| 647 | func->name = name; | ||
| 648 | func->phandle = data[0]; | ||
| 649 | func->flags = data[1]; | ||
| 650 | data += 2; | ||
| 651 | length -= 8; | ||
| 652 | func->data = data; | ||
| 653 | func->length = length; | ||
| 654 | func->dev = dev; | ||
| 655 | DBG("pmf: idx %d: flags=%08x, phandle=%08x " | ||
| 656 | " %d bytes remaining, parsing...\n", | ||
| 657 | count+1, func->flags, func->phandle, length); | ||
| 658 | if (pmf_parse_one(func, NULL, NULL, NULL)) { | ||
| 659 | kfree(func); | ||
| 660 | goto bail; | ||
| 661 | } | ||
| 662 | length -= func->length; | ||
| 663 | data = (u32 *)(((u8 *)data) + func->length); | ||
| 664 | list_add(&func->link, &dev->functions); | ||
| 665 | pmf_get_device(dev); | ||
| 666 | count++; | ||
| 667 | } | ||
| 668 | bail: | ||
| 669 | DBG("pmf: Added %d functions\n", count); | ||
| 670 | |||
| 671 | return count; | ||
| 672 | } | ||
| 673 | |||
| 674 | static int pmf_add_functions(struct pmf_device *dev, void *driverdata) | ||
| 675 | { | ||
| 676 | struct property *pp; | ||
| 677 | #define PP_PREFIX "platform-do-" | ||
| 678 | const int plen = strlen(PP_PREFIX); | ||
| 679 | int count = 0; | ||
| 680 | |||
| 681 | for (pp = dev->node->properties; pp != 0; pp = pp->next) { | ||
| 682 | char *name; | ||
| 683 | if (strncmp(pp->name, PP_PREFIX, plen) != 0) | ||
| 684 | continue; | ||
| 685 | name = pp->name + plen; | ||
| 686 | if (strlen(name) && pp->length >= 12) | ||
| 687 | count += pmf_add_function_prop(dev, driverdata, name, | ||
| 688 | (u32 *)pp->value, | ||
| 689 | pp->length); | ||
| 690 | } | ||
| 691 | return count; | ||
| 692 | } | ||
| 693 | |||
| 694 | |||
| 695 | int pmf_register_driver(struct device_node *np, | ||
| 696 | struct pmf_handlers *handlers, | ||
| 697 | void *driverdata) | ||
| 698 | { | ||
| 699 | struct pmf_device *dev; | ||
| 700 | unsigned long flags; | ||
| 701 | int rc = 0; | ||
| 702 | |||
| 703 | if (handlers == NULL) | ||
| 704 | return -EINVAL; | ||
| 705 | |||
| 706 | DBG("pmf: registering driver for node %s\n", np->full_name); | ||
| 707 | |||
| 708 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 709 | dev = pmf_find_device(np); | ||
| 710 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 711 | if (dev != NULL) { | ||
| 712 | DBG("pmf: already there !\n"); | ||
| 713 | pmf_put_device(dev); | ||
| 714 | return -EBUSY; | ||
| 715 | } | ||
| 716 | |||
| 717 | dev = kzalloc(sizeof(struct pmf_device), GFP_KERNEL); | ||
| 718 | if (dev == NULL) { | ||
| 719 | DBG("pmf: no memory !\n"); | ||
| 720 | return -ENOMEM; | ||
| 721 | } | ||
| 722 | kref_init(&dev->ref); | ||
| 723 | dev->node = of_node_get(np); | ||
| 724 | dev->handlers = handlers; | ||
| 725 | INIT_LIST_HEAD(&dev->functions); | ||
| 726 | |||
| 727 | rc = pmf_add_functions(dev, driverdata); | ||
| 728 | if (rc == 0) { | ||
| 729 | DBG("pmf: no functions, disposing.. \n"); | ||
| 730 | of_node_put(np); | ||
| 731 | kfree(dev); | ||
| 732 | return -ENODEV; | ||
| 733 | } | ||
| 734 | |||
| 735 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 736 | list_add(&dev->link, &pmf_devices); | ||
| 737 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 738 | |||
| 739 | return 0; | ||
| 740 | } | ||
| 741 | EXPORT_SYMBOL_GPL(pmf_register_driver); | ||
| 742 | |||
| 743 | struct pmf_function *pmf_get_function(struct pmf_function *func) | ||
| 744 | { | ||
| 745 | if (!try_module_get(func->dev->handlers->owner)) | ||
| 746 | return NULL; | ||
| 747 | kref_get(&func->ref); | ||
| 748 | return func; | ||
| 749 | } | ||
| 750 | EXPORT_SYMBOL_GPL(pmf_get_function); | ||
| 751 | |||
| 752 | static void pmf_release_function(struct kref *kref) | ||
| 753 | { | ||
| 754 | struct pmf_function *func = | ||
| 755 | container_of(kref, struct pmf_function, ref); | ||
| 756 | pmf_put_device(func->dev); | ||
| 757 | kfree(func); | ||
| 758 | } | ||
| 759 | |||
| 760 | static inline void __pmf_put_function(struct pmf_function *func) | ||
| 761 | { | ||
| 762 | kref_put(&func->ref, pmf_release_function); | ||
| 763 | } | ||
| 764 | |||
| 765 | void pmf_put_function(struct pmf_function *func) | ||
| 766 | { | ||
| 767 | if (func == NULL) | ||
| 768 | return; | ||
| 769 | module_put(func->dev->handlers->owner); | ||
| 770 | __pmf_put_function(func); | ||
| 771 | } | ||
| 772 | EXPORT_SYMBOL_GPL(pmf_put_function); | ||
| 773 | |||
| 774 | void pmf_unregister_driver(struct device_node *np) | ||
| 775 | { | ||
| 776 | struct pmf_device *dev; | ||
| 777 | unsigned long flags; | ||
| 778 | |||
| 779 | DBG("pmf: unregistering driver for node %s\n", np->full_name); | ||
| 780 | |||
| 781 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 782 | dev = pmf_find_device(np); | ||
| 783 | if (dev == NULL) { | ||
| 784 | DBG("pmf: not such driver !\n"); | ||
| 785 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 786 | return; | ||
| 787 | } | ||
| 788 | list_del(&dev->link); | ||
| 789 | |||
| 790 | while(!list_empty(&dev->functions)) { | ||
| 791 | struct pmf_function *func = | ||
| 792 | list_entry(dev->functions.next, typeof(*func), link); | ||
| 793 | list_del(&func->link); | ||
| 794 | __pmf_put_function(func); | ||
| 795 | } | ||
| 796 | |||
| 797 | pmf_put_device(dev); | ||
| 798 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 799 | } | ||
| 800 | EXPORT_SYMBOL_GPL(pmf_unregister_driver); | ||
| 801 | |||
| 802 | struct pmf_function *__pmf_find_function(struct device_node *target, | ||
| 803 | const char *name, u32 flags) | ||
| 804 | { | ||
| 805 | struct device_node *actor = of_node_get(target); | ||
| 806 | struct pmf_device *dev; | ||
| 807 | struct pmf_function *func, *result = NULL; | ||
| 808 | char fname[64]; | ||
| 809 | u32 *prop, ph; | ||
| 810 | |||
| 811 | /* | ||
| 812 | * Look for a "platform-*" function reference. If we can't find | ||
| 813 | * one, then we fallback to a direct call attempt | ||
| 814 | */ | ||
| 815 | snprintf(fname, 63, "platform-%s", name); | ||
| 816 | prop = (u32 *)get_property(target, fname, NULL); | ||
| 817 | if (prop == NULL) | ||
| 818 | goto find_it; | ||
| 819 | ph = *prop; | ||
| 820 | if (ph == 0) | ||
| 821 | goto find_it; | ||
| 822 | |||
| 823 | /* | ||
| 824 | * Ok, now try to find the actor. If we can't find it, we fail, | ||
| 825 | * there is no point in falling back there | ||
| 826 | */ | ||
| 827 | of_node_put(actor); | ||
| 828 | actor = of_find_node_by_phandle(ph); | ||
| 829 | if (actor == NULL) | ||
| 830 | return NULL; | ||
| 831 | find_it: | ||
| 832 | dev = pmf_find_device(actor); | ||
| 833 | if (dev == NULL) | ||
| 834 | return NULL; | ||
| 835 | |||
| 836 | list_for_each_entry(func, &dev->functions, link) { | ||
| 837 | if (name && strcmp(name, func->name)) | ||
| 838 | continue; | ||
| 839 | if (func->phandle && target->node != func->phandle) | ||
| 840 | continue; | ||
| 841 | if ((func->flags & flags) == 0) | ||
| 842 | continue; | ||
| 843 | result = func; | ||
| 844 | break; | ||
| 845 | } | ||
| 846 | of_node_put(actor); | ||
| 847 | pmf_put_device(dev); | ||
| 848 | return result; | ||
| 849 | } | ||
| 850 | |||
| 851 | |||
| 852 | int pmf_register_irq_client(struct device_node *target, | ||
| 853 | const char *name, | ||
| 854 | struct pmf_irq_client *client) | ||
| 855 | { | ||
| 856 | struct pmf_function *func; | ||
| 857 | unsigned long flags; | ||
| 858 | |||
| 859 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 860 | func = __pmf_find_function(target, name, PMF_FLAGS_INT_GEN); | ||
| 861 | if (func == NULL) { | ||
| 862 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 863 | return -ENODEV; | ||
| 864 | } | ||
| 865 | list_add(&client->link, &func->irq_clients); | ||
| 866 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 867 | |||
| 868 | return 0; | ||
| 869 | } | ||
| 870 | EXPORT_SYMBOL_GPL(pmf_register_irq_client); | ||
| 871 | |||
| 872 | void pmf_unregister_irq_client(struct device_node *np, | ||
| 873 | const char *name, | ||
| 874 | struct pmf_irq_client *client) | ||
| 875 | { | ||
| 876 | unsigned long flags; | ||
| 877 | |||
| 878 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 879 | list_del(&client->link); | ||
| 880 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 881 | } | ||
| 882 | EXPORT_SYMBOL_GPL(pmf_unregister_irq_client); | ||
| 883 | |||
| 884 | |||
| 885 | void pmf_do_irq(struct pmf_function *func) | ||
| 886 | { | ||
| 887 | unsigned long flags; | ||
| 888 | struct pmf_irq_client *client; | ||
| 889 | |||
| 890 | /* For now, using a spinlock over the whole function. Can be made | ||
| 891 | * to drop the lock using 2 lists if necessary | ||
| 892 | */ | ||
| 893 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 894 | list_for_each_entry(client, &func->irq_clients, link) { | ||
| 895 | if (!try_module_get(client->owner)) | ||
| 896 | continue; | ||
| 897 | client->handler(client->data); | ||
| 898 | module_put(client->owner); | ||
| 899 | } | ||
| 900 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 901 | } | ||
| 902 | EXPORT_SYMBOL_GPL(pmf_do_irq); | ||
| 903 | |||
| 904 | |||
| 905 | int pmf_call_one(struct pmf_function *func, struct pmf_args *args) | ||
| 906 | { | ||
| 907 | struct pmf_device *dev = func->dev; | ||
| 908 | void *instdata = NULL; | ||
| 909 | int rc = 0; | ||
| 910 | |||
| 911 | DBG(" ** pmf_call_one(%s/%s) **\n", dev->node->full_name, func->name); | ||
| 912 | |||
| 913 | if (dev->handlers->begin) | ||
| 914 | instdata = dev->handlers->begin(func, args); | ||
| 915 | rc = pmf_parse_one(func, dev->handlers, instdata, args); | ||
| 916 | if (dev->handlers->end) | ||
| 917 | dev->handlers->end(func, instdata); | ||
| 918 | |||
| 919 | return rc; | ||
| 920 | } | ||
| 921 | EXPORT_SYMBOL_GPL(pmf_call_one); | ||
| 922 | |||
| 923 | int pmf_do_functions(struct device_node *np, const char *name, | ||
| 924 | u32 phandle, u32 fflags, struct pmf_args *args) | ||
| 925 | { | ||
| 926 | struct pmf_device *dev; | ||
| 927 | struct pmf_function *func, *tmp; | ||
| 928 | unsigned long flags; | ||
| 929 | int rc = -ENODEV; | ||
| 930 | |||
| 931 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 932 | |||
| 933 | dev = pmf_find_device(np); | ||
| 934 | if (dev == NULL) { | ||
| 935 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 936 | return -ENODEV; | ||
| 937 | } | ||
| 938 | list_for_each_entry_safe(func, tmp, &dev->functions, link) { | ||
| 939 | if (name && strcmp(name, func->name)) | ||
| 940 | continue; | ||
| 941 | if (phandle && func->phandle && phandle != func->phandle) | ||
| 942 | continue; | ||
| 943 | if ((func->flags & fflags) == 0) | ||
| 944 | continue; | ||
| 945 | if (pmf_get_function(func) == NULL) | ||
| 946 | continue; | ||
| 947 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 948 | rc = pmf_call_one(func, args); | ||
| 949 | pmf_put_function(func); | ||
| 950 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 951 | } | ||
| 952 | pmf_put_device(dev); | ||
| 953 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 954 | |||
| 955 | return rc; | ||
| 956 | } | ||
| 957 | EXPORT_SYMBOL_GPL(pmf_do_functions); | ||
| 958 | |||
| 959 | |||
| 960 | struct pmf_function *pmf_find_function(struct device_node *target, | ||
| 961 | const char *name) | ||
| 962 | { | ||
| 963 | struct pmf_function *func; | ||
| 964 | unsigned long flags; | ||
| 965 | |||
| 966 | spin_lock_irqsave(&pmf_lock, flags); | ||
| 967 | func = __pmf_find_function(target, name, PMF_FLAGS_ON_DEMAND); | ||
| 968 | if (func) | ||
| 969 | func = pmf_get_function(func); | ||
| 970 | spin_unlock_irqrestore(&pmf_lock, flags); | ||
| 971 | return func; | ||
| 972 | } | ||
| 973 | EXPORT_SYMBOL_GPL(pmf_find_function); | ||
| 974 | |||
| 975 | int pmf_call_function(struct device_node *target, const char *name, | ||
| 976 | struct pmf_args *args) | ||
| 977 | { | ||
| 978 | struct pmf_function *func = pmf_find_function(target, name); | ||
| 979 | int rc; | ||
| 980 | |||
| 981 | if (func == NULL) | ||
| 982 | return -ENODEV; | ||
| 983 | |||
| 984 | rc = pmf_call_one(func, args); | ||
| 985 | pmf_put_function(func); | ||
| 986 | return rc; | ||
| 987 | } | ||
| 988 | EXPORT_SYMBOL_GPL(pmf_call_function); | ||
| 989 | |||
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index ab72ba86be1e..0df2cdcd805c 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c | |||
| @@ -52,8 +52,9 @@ | |||
| 52 | #include <asm/cacheflush.h> | 52 | #include <asm/cacheflush.h> |
| 53 | #include <asm/keylargo.h> | 53 | #include <asm/keylargo.h> |
| 54 | #include <asm/pmac_low_i2c.h> | 54 | #include <asm/pmac_low_i2c.h> |
| 55 | #include <asm/pmac_pfunc.h> | ||
| 55 | 56 | ||
| 56 | #undef DEBUG | 57 | #define DEBUG |
| 57 | 58 | ||
| 58 | #ifdef DEBUG | 59 | #ifdef DEBUG |
| 59 | #define DBG(fmt...) udbg_printf(fmt) | 60 | #define DBG(fmt...) udbg_printf(fmt) |
| @@ -62,6 +63,7 @@ | |||
| 62 | #endif | 63 | #endif |
| 63 | 64 | ||
| 64 | extern void __secondary_start_pmac_0(void); | 65 | extern void __secondary_start_pmac_0(void); |
| 66 | extern int pmac_pfunc_base_install(void); | ||
| 65 | 67 | ||
| 66 | #ifdef CONFIG_PPC32 | 68 | #ifdef CONFIG_PPC32 |
| 67 | 69 | ||
| @@ -602,11 +604,29 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) | |||
| 602 | pmac_tb_clock_chip_host = NULL; | 604 | pmac_tb_clock_chip_host = NULL; |
| 603 | } | 605 | } |
| 604 | 606 | ||
| 605 | #endif /* CONFIG_PPC64 */ | ||
| 606 | 607 | ||
| 607 | 608 | ||
| 608 | /* | 609 | /* |
| 609 | * SMP G4 and newer G5 use a GPIO to enable/disable the timebase. | 610 | * Newer G5s uses a platform function |
| 611 | */ | ||
| 612 | |||
| 613 | static void smp_core99_pfunc_tb_freeze(int freeze) | ||
| 614 | { | ||
| 615 | struct device_node *cpus; | ||
| 616 | struct pmf_args args; | ||
| 617 | |||
| 618 | cpus = of_find_node_by_path("/cpus"); | ||
| 619 | BUG_ON(cpus == NULL); | ||
| 620 | args.count = 1; | ||
| 621 | args.u[0].v = !freeze; | ||
| 622 | pmf_call_function(cpus, "cpu-timebase", &args); | ||
| 623 | of_node_put(cpus); | ||
| 624 | } | ||
| 625 | |||
| 626 | #else /* CONFIG_PPC64 */ | ||
| 627 | |||
| 628 | /* | ||
| 629 | * SMP G4 use a GPIO to enable/disable the timebase. | ||
| 610 | */ | 630 | */ |
| 611 | 631 | ||
| 612 | static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */ | 632 | static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */ |
| @@ -620,6 +640,9 @@ static void smp_core99_gpio_tb_freeze(int freeze) | |||
| 620 | pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); | 640 | pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); |
| 621 | } | 641 | } |
| 622 | 642 | ||
| 643 | |||
| 644 | #endif /* !CONFIG_PPC64 */ | ||
| 645 | |||
| 623 | /* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */ | 646 | /* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */ |
| 624 | volatile static long int core99_l2_cache; | 647 | volatile static long int core99_l2_cache; |
| 625 | volatile static long int core99_l3_cache; | 648 | volatile static long int core99_l3_cache; |
| @@ -665,19 +688,15 @@ static void __init smp_core99_setup(int ncpus) | |||
| 665 | machine_is_compatible("RackMac3,1")) | 688 | machine_is_compatible("RackMac3,1")) |
| 666 | smp_core99_setup_i2c_hwsync(ncpus); | 689 | smp_core99_setup_i2c_hwsync(ncpus); |
| 667 | 690 | ||
| 668 | /* GPIO based HW sync on recent G5s */ | 691 | /* pfunc based HW sync on recent G5s */ |
| 669 | if (pmac_tb_freeze == NULL) { | 692 | if (pmac_tb_freeze == NULL) { |
| 670 | struct device_node *np = | 693 | struct device_node *cpus = |
| 671 | of_find_node_by_name(NULL, "timebase-enable"); | 694 | of_find_node_by_path("/cpus"); |
| 672 | u32 *reg = (u32 *)get_property(np, "reg", NULL); | 695 | if (cpus && |
| 673 | 696 | get_property(cpus, "platform-cpu-timebase", NULL)) { | |
| 674 | if (np && reg && !strcmp(np->type, "gpio")) { | 697 | pmac_tb_freeze = smp_core99_pfunc_tb_freeze; |
| 675 | core99_tb_gpio = *reg; | ||
| 676 | if (core99_tb_gpio < 0x50) | ||
| 677 | core99_tb_gpio += 0x50; | ||
| 678 | pmac_tb_freeze = smp_core99_gpio_tb_freeze; | ||
| 679 | printk(KERN_INFO "Processor timebase sync using" | 698 | printk(KERN_INFO "Processor timebase sync using" |
| 680 | " GPIO 0x%02x\n", core99_tb_gpio); | 699 | " platform function\n"); |
| 681 | } | 700 | } |
| 682 | } | 701 | } |
| 683 | 702 | ||
| @@ -746,6 +765,7 @@ static int __init smp_core99_probe(void) | |||
| 746 | /* We need to perform some early initialisations before we can start | 765 | /* We need to perform some early initialisations before we can start |
| 747 | * setting up SMP as we are running before initcalls | 766 | * setting up SMP as we are running before initcalls |
| 748 | */ | 767 | */ |
| 768 | pmac_pfunc_base_install(); | ||
| 749 | pmac_i2c_init(); | 769 | pmac_i2c_init(); |
| 750 | 770 | ||
| 751 | /* Setup various bits like timebase sync method, ability to nap, ... */ | 771 | /* Setup various bits like timebase sync method, ability to nap, ... */ |
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index aa481a88ccab..6eb93e45fcd3 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c | |||
| @@ -55,6 +55,8 @@ | |||
| 55 | #include <asm/sections.h> | 55 | #include <asm/sections.h> |
| 56 | #include <asm/irq.h> | 56 | #include <asm/irq.h> |
| 57 | #include <asm/pmac_feature.h> | 57 | #include <asm/pmac_feature.h> |
| 58 | #include <asm/pmac_pfunc.h> | ||
| 59 | #include <asm/pmac_low_i2c.h> | ||
| 58 | #include <asm/uaccess.h> | 60 | #include <asm/uaccess.h> |
| 59 | #include <asm/mmu_context.h> | 61 | #include <asm/mmu_context.h> |
| 60 | #include <asm/cputable.h> | 62 | #include <asm/cputable.h> |
| @@ -2105,6 +2107,10 @@ pmac_suspend_devices(void) | |||
| 2105 | return -EBUSY; | 2107 | return -EBUSY; |
| 2106 | } | 2108 | } |
| 2107 | 2109 | ||
| 2110 | /* Call platform functions marked "on sleep" */ | ||
| 2111 | pmac_pfunc_i2c_suspend(); | ||
| 2112 | pmac_pfunc_base_suspend(); | ||
| 2113 | |||
| 2108 | /* Stop preemption */ | 2114 | /* Stop preemption */ |
| 2109 | preempt_disable(); | 2115 | preempt_disable(); |
| 2110 | 2116 | ||
| @@ -2175,6 +2181,10 @@ pmac_wakeup_devices(void) | |||
| 2175 | mdelay(10); | 2181 | mdelay(10); |
| 2176 | preempt_enable(); | 2182 | preempt_enable(); |
| 2177 | 2183 | ||
| 2184 | /* Call platform functions marked "on wake" */ | ||
| 2185 | pmac_pfunc_base_resume(); | ||
| 2186 | pmac_pfunc_i2c_resume(); | ||
| 2187 | |||
| 2178 | /* Resume devices */ | 2188 | /* Resume devices */ |
| 2179 | device_resume(); | 2189 | device_resume(); |
| 2180 | 2190 | ||
diff --git a/include/asm-powerpc/pmac_feature.h b/include/asm-powerpc/pmac_feature.h index e654ad0e5b42..3221628130c4 100644 --- a/include/asm-powerpc/pmac_feature.h +++ b/include/asm-powerpc/pmac_feature.h | |||
| @@ -374,5 +374,24 @@ extern struct macio_chip* macio_find(struct device_node* child, int type); | |||
| 374 | #define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r))) | 374 | #define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r))) |
| 375 | #define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v))) | 375 | #define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v))) |
| 376 | 376 | ||
| 377 | /* | ||
| 378 | * Those are exported by pmac feature for internal use by arch code | ||
| 379 | * only like the platform function callbacks, do not use directly in drivers | ||
| 380 | */ | ||
| 381 | extern spinlock_t feature_lock; | ||
| 382 | extern struct device_node *uninorth_node; | ||
| 383 | extern u32 __iomem *uninorth_base; | ||
| 384 | |||
| 385 | /* | ||
| 386 | * Uninorth reg. access. Note that Uni-N regs are big endian | ||
| 387 | */ | ||
| 388 | |||
| 389 | #define UN_REG(r) (uninorth_base + ((r) >> 2)) | ||
| 390 | #define UN_IN(r) (in_be32(UN_REG(r))) | ||
| 391 | #define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) | ||
| 392 | #define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) | ||
| 393 | #define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) | ||
| 394 | |||
| 395 | |||
| 377 | #endif /* __PPC_ASM_PMAC_FEATURE_H */ | 396 | #endif /* __PPC_ASM_PMAC_FEATURE_H */ |
| 378 | #endif /* __KERNEL__ */ | 397 | #endif /* __KERNEL__ */ |
diff --git a/include/asm-powerpc/pmac_low_i2c.h b/include/asm-powerpc/pmac_low_i2c.h index 480018f41e1a..131011bd7e76 100644 --- a/include/asm-powerpc/pmac_low_i2c.h +++ b/include/asm-powerpc/pmac_low_i2c.h | |||
| @@ -99,6 +99,9 @@ extern int pmac_i2c_setmode(struct pmac_i2c_bus *bus, int mode); | |||
| 99 | extern int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, | 99 | extern int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, |
| 100 | u32 subaddr, u8 *data, int len); | 100 | u32 subaddr, u8 *data, int len); |
| 101 | 101 | ||
| 102 | /* Suspend/resume code called by via-pmu directly for now */ | ||
| 103 | extern void pmac_pfunc_i2c_suspend(void); | ||
| 104 | extern void pmac_pfunc_i2c_resume(void); | ||
| 102 | 105 | ||
| 103 | #endif /* __KERNEL__ */ | 106 | #endif /* __KERNEL__ */ |
| 104 | #endif /* __PMAC_LOW_I2C_H__ */ | 107 | #endif /* __PMAC_LOW_I2C_H__ */ |
diff --git a/include/asm-powerpc/pmac_pfunc.h b/include/asm-powerpc/pmac_pfunc.h new file mode 100644 index 000000000000..d9728c80f86d --- /dev/null +++ b/include/asm-powerpc/pmac_pfunc.h | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | #ifndef __PMAC_PFUNC_H__ | ||
| 2 | #define __PMAC_PFUNC_H__ | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include <linux/list.h> | ||
| 6 | |||
| 7 | /* Flags in command lists */ | ||
| 8 | #define PMF_FLAGS_ON_INIT 0x80000000u | ||
| 9 | #define PMF_FLGAS_ON_TERM 0x40000000u | ||
| 10 | #define PMF_FLAGS_ON_SLEEP 0x20000000u | ||
| 11 | #define PMF_FLAGS_ON_WAKE 0x10000000u | ||
| 12 | #define PMF_FLAGS_ON_DEMAND 0x08000000u | ||
| 13 | #define PMF_FLAGS_INT_GEN 0x04000000u | ||
| 14 | #define PMF_FLAGS_HIGH_SPEED 0x02000000u | ||
| 15 | #define PMF_FLAGS_LOW_SPEED 0x01000000u | ||
| 16 | #define PMF_FLAGS_SIDE_EFFECTS 0x00800000u | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Arguments to a platform function call. | ||
| 20 | * | ||
| 21 | * NOTE: By convention, pointer arguments point to an u32 | ||
| 22 | */ | ||
| 23 | struct pmf_args { | ||
| 24 | union { | ||
| 25 | u32 v; | ||
| 26 | u32 *p; | ||
| 27 | } u[4]; | ||
| 28 | unsigned int count; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* | ||
| 32 | * A driver capable of interpreting commands provides a handlers | ||
| 33 | * structure filled with whatever handlers are implemented by this | ||
| 34 | * driver. Non implemented handlers are left NULL. | ||
| 35 | * | ||
| 36 | * PMF_STD_ARGS are the same arguments that are passed to the parser | ||
| 37 | * and that gets passed back to the various handlers. | ||
| 38 | * | ||
| 39 | * Interpreting a given function always start with a begin() call which | ||
| 40 | * returns an instance data to be passed around subsequent calls, and | ||
| 41 | * ends with an end() call. This allows the low level driver to implement | ||
| 42 | * locking policy or per-function instance data. | ||
| 43 | * | ||
| 44 | * For interrupt capable functions, irq_enable() is called when a client | ||
| 45 | * registers, and irq_disable() is called when the last client unregisters | ||
| 46 | * Note that irq_enable & irq_disable are called within a semaphore held | ||
| 47 | * by the core, thus you should not try to register yourself to some other | ||
| 48 | * pmf interrupt during those calls. | ||
| 49 | */ | ||
| 50 | |||
| 51 | #define PMF_STD_ARGS struct pmf_function *func, void *instdata, \ | ||
| 52 | struct pmf_args *args | ||
| 53 | |||
| 54 | struct pmf_function; | ||
| 55 | |||
| 56 | struct pmf_handlers { | ||
| 57 | void * (*begin)(struct pmf_function *func, struct pmf_args *args); | ||
| 58 | void (*end)(struct pmf_function *func, void *instdata); | ||
| 59 | |||
| 60 | int (*irq_enable)(struct pmf_function *func); | ||
| 61 | int (*irq_disable)(struct pmf_function *func); | ||
| 62 | |||
| 63 | int (*write_gpio)(PMF_STD_ARGS, u8 value, u8 mask); | ||
| 64 | int (*read_gpio)(PMF_STD_ARGS, u8 mask, int rshift, u8 xor); | ||
| 65 | |||
| 66 | int (*write_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); | ||
| 67 | int (*read_reg32)(PMF_STD_ARGS, u32 offset); | ||
| 68 | int (*write_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); | ||
| 69 | int (*read_reg16)(PMF_STD_ARGS, u32 offset); | ||
| 70 | int (*write_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); | ||
| 71 | int (*read_reg8)(PMF_STD_ARGS, u32 offset); | ||
| 72 | |||
| 73 | int (*delay)(PMF_STD_ARGS, u32 duration); | ||
| 74 | |||
| 75 | int (*wait_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); | ||
| 76 | int (*wait_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); | ||
| 77 | int (*wait_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); | ||
| 78 | |||
| 79 | int (*read_i2c)(PMF_STD_ARGS, u32 len); | ||
| 80 | int (*write_i2c)(PMF_STD_ARGS, u32 len, const u8 *data); | ||
| 81 | int (*rmw_i2c)(PMF_STD_ARGS, u32 masklen, u32 valuelen, u32 totallen, | ||
| 82 | const u8 *maskdata, const u8 *valuedata); | ||
| 83 | |||
| 84 | int (*read_cfg)(PMF_STD_ARGS, u32 offset, u32 len); | ||
| 85 | int (*write_cfg)(PMF_STD_ARGS, u32 offset, u32 len, const u8 *data); | ||
| 86 | int (*rmw_cfg)(PMF_STD_ARGS, u32 offset, u32 masklen, u32 valuelen, | ||
| 87 | u32 totallen, const u8 *maskdata, const u8 *valuedata); | ||
| 88 | |||
| 89 | int (*read_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len); | ||
| 90 | int (*write_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len, const u8 *data); | ||
| 91 | int (*set_i2c_mode)(PMF_STD_ARGS, int mode); | ||
| 92 | int (*rmw_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 masklen, u32 valuelen, | ||
| 93 | u32 totallen, const u8 *maskdata, | ||
| 94 | const u8 *valuedata); | ||
| 95 | |||
| 96 | int (*read_reg32_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, | ||
| 97 | u32 xor); | ||
| 98 | int (*read_reg16_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, | ||
| 99 | u32 xor); | ||
| 100 | int (*read_reg8_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, | ||
| 101 | u32 xor); | ||
| 102 | |||
| 103 | int (*write_reg32_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); | ||
| 104 | int (*write_reg16_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); | ||
| 105 | int (*write_reg8_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); | ||
| 106 | |||
| 107 | int (*mask_and_compare)(PMF_STD_ARGS, u32 len, const u8 *maskdata, | ||
| 108 | const u8 *valuedata); | ||
| 109 | |||
| 110 | struct module *owner; | ||
| 111 | }; | ||
| 112 | |||
| 113 | |||
| 114 | /* | ||
| 115 | * Drivers who expose platform functions register at init time, this | ||
| 116 | * causes the platform functions for that device node to be parsed in | ||
| 117 | * advance and associated with the device. The data structures are | ||
| 118 | * partially public so a driver can walk the list of platform functions | ||
| 119 | * and eventually inspect the flags | ||
| 120 | */ | ||
| 121 | struct pmf_device; | ||
| 122 | |||
| 123 | struct pmf_function { | ||
| 124 | /* All functions for a given driver are linked */ | ||
| 125 | struct list_head link; | ||
| 126 | |||
| 127 | /* Function node & driver data */ | ||
| 128 | struct device_node *node; | ||
| 129 | void *driver_data; | ||
| 130 | |||
| 131 | /* For internal use by core */ | ||
| 132 | struct pmf_device *dev; | ||
| 133 | |||
| 134 | /* The name is the "xxx" in "platform-do-xxx", this is how | ||
| 135 | * platform functions are identified by this code. Some functions | ||
| 136 | * only operate for a given target, in which case the phandle is | ||
| 137 | * here (or 0 if the filter doesn't apply) | ||
| 138 | */ | ||
| 139 | const char *name; | ||
| 140 | u32 phandle; | ||
| 141 | |||
| 142 | /* The flags for that function. You can have several functions | ||
| 143 | * with the same name and different flag | ||
| 144 | */ | ||
| 145 | u32 flags; | ||
| 146 | |||
| 147 | /* The actual tokenized function blob */ | ||
| 148 | const void *data; | ||
| 149 | unsigned int length; | ||
| 150 | |||
| 151 | /* Interrupt clients */ | ||
| 152 | struct list_head irq_clients; | ||
| 153 | |||
| 154 | /* Refcounting */ | ||
| 155 | struct kref ref; | ||
| 156 | }; | ||
| 157 | |||
| 158 | /* | ||
| 159 | * For platform functions that are interrupts, one can register | ||
| 160 | * irq_client structures. You canNOT use the same structure twice | ||
| 161 | * as it contains a link member. Also, the callback is called with | ||
| 162 | * a spinlock held, you must not call back into any of the pmf_* functions | ||
| 163 | * from within that callback | ||
| 164 | */ | ||
| 165 | struct pmf_irq_client { | ||
| 166 | void (*handler)(void *data); | ||
| 167 | void *data; | ||
| 168 | struct module *owner; | ||
| 169 | struct list_head link; | ||
| 170 | }; | ||
| 171 | |||
| 172 | |||
| 173 | /* | ||
| 174 | * Register/Unregister a function-capable driver and its handlers | ||
| 175 | */ | ||
| 176 | extern int pmf_register_driver(struct device_node *np, | ||
| 177 | struct pmf_handlers *handlers, | ||
| 178 | void *driverdata); | ||
| 179 | |||
| 180 | extern void pmf_unregister_driver(struct device_node *np); | ||
| 181 | |||
| 182 | |||
| 183 | /* | ||
| 184 | * Register/Unregister interrupt clients | ||
| 185 | */ | ||
| 186 | extern int pmf_register_irq_client(struct device_node *np, | ||
| 187 | const char *name, | ||
| 188 | struct pmf_irq_client *client); | ||
| 189 | |||
| 190 | extern void pmf_unregister_irq_client(struct device_node *np, | ||
| 191 | const char *name, | ||
| 192 | struct pmf_irq_client *client); | ||
| 193 | |||
| 194 | /* | ||
| 195 | * Called by the handlers when an irq happens | ||
| 196 | */ | ||
| 197 | extern void pmf_do_irq(struct pmf_function *func); | ||
| 198 | |||
| 199 | |||
| 200 | /* | ||
| 201 | * Low level call to platform functions. | ||
| 202 | * | ||
| 203 | * The phandle can filter on the target object for functions that have | ||
| 204 | * multiple targets, the flags allow you to restrict the call to a given | ||
| 205 | * combination of flags. | ||
| 206 | * | ||
| 207 | * The args array contains as many arguments as is required by the function, | ||
| 208 | * this is dependent on the function you are calling, unfortunately Apple | ||
| 209 | * mecanism provides no way to encode that so you have to get it right at | ||
| 210 | * the call site. Some functions require no args, in which case, you can | ||
| 211 | * pass NULL. | ||
| 212 | * | ||
| 213 | * You can also pass NULL to the name. This will match any function that has | ||
| 214 | * the appropriate combination of flags & phandle or you can pass 0 to the | ||
| 215 | * phandle to match any | ||
| 216 | */ | ||
| 217 | extern int pmf_do_functions(struct device_node *np, const char *name, | ||
| 218 | u32 phandle, u32 flags, struct pmf_args *args); | ||
| 219 | |||
| 220 | |||
| 221 | |||
| 222 | /* | ||
| 223 | * High level call to a platform function. | ||
| 224 | * | ||
| 225 | * This one looks for the platform-xxx first so you should call it to the | ||
| 226 | * actual target if any. It will fallback to platform-do-xxx if it can't | ||
| 227 | * find one. It will also exclusively target functions that have | ||
| 228 | * the "OnDemand" flag. | ||
| 229 | */ | ||
| 230 | |||
| 231 | extern int pmf_call_function(struct device_node *target, const char *name, | ||
| 232 | struct pmf_args *args); | ||
| 233 | |||
| 234 | |||
| 235 | /* | ||
| 236 | * For low latency interrupt usage, you can lookup for on-demand functions | ||
| 237 | * using the functions below | ||
| 238 | */ | ||
| 239 | |||
| 240 | extern struct pmf_function *pmf_find_function(struct device_node *target, | ||
| 241 | const char *name); | ||
| 242 | |||
| 243 | extern struct pmf_function * pmf_get_function(struct pmf_function *func); | ||
| 244 | extern void pmf_put_function(struct pmf_function *func); | ||
| 245 | |||
| 246 | extern int pmf_call_one(struct pmf_function *func, struct pmf_args *args); | ||
| 247 | |||
| 248 | |||
| 249 | /* Suspend/resume code called by via-pmu directly for now */ | ||
| 250 | extern void pmac_pfunc_base_suspend(void); | ||
| 251 | extern void pmac_pfunc_base_resume(void); | ||
| 252 | |||
| 253 | #endif /* __PMAC_PFUNC_H__ */ | ||
