18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device driver for the PMU in Apple PowerBooks and PowerMacs. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The VIA (versatile interface adapter) interfaces to the PMU, 68c2ecf20Sopenharmony_ci * a 6805 microprocessor core whose primary function is to control 78c2ecf20Sopenharmony_ci * battery charging and system power on the PowerBook 3400 and 2400. 88c2ecf20Sopenharmony_ci * The PMU also controls the ADB (Apple Desktop Bus) which connects 98c2ecf20Sopenharmony_ci * to the keyboard and mouse, as well as the non-volatile RAM 108c2ecf20Sopenharmony_ci * and the RTC (real time clock) chip. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. 138c2ecf20Sopenharmony_ci * Copyright (C) 2001-2002 Benjamin Herrenschmidt 148c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Johannes Berg 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THIS DRIVER IS BECOMING A TOTAL MESS ! 178c2ecf20Sopenharmony_ci * - Cleanup atomically disabling reply to PMU events after 188c2ecf20Sopenharmony_ci * a sleep or a freq. switch 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci#include <stdarg.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/types.h> 248c2ecf20Sopenharmony_ci#include <linux/errno.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 288c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 298c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 308c2ecf20Sopenharmony_ci#include <linux/pci.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include <linux/poll.h> 338c2ecf20Sopenharmony_ci#include <linux/adb.h> 348c2ecf20Sopenharmony_ci#include <linux/pmu.h> 358c2ecf20Sopenharmony_ci#include <linux/cuda.h> 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 388c2ecf20Sopenharmony_ci#include <linux/pm.h> 398c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 408c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 418c2ecf20Sopenharmony_ci#include <linux/init.h> 428c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 438c2ecf20Sopenharmony_ci#include <linux/device.h> 448c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 458c2ecf20Sopenharmony_ci#include <linux/freezer.h> 468c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 478c2ecf20Sopenharmony_ci#include <linux/suspend.h> 488c2ecf20Sopenharmony_ci#include <linux/cpu.h> 498c2ecf20Sopenharmony_ci#include <linux/compat.h> 508c2ecf20Sopenharmony_ci#include <linux/of_address.h> 518c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 528c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 538c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 548c2ecf20Sopenharmony_ci#include <asm/machdep.h> 558c2ecf20Sopenharmony_ci#include <asm/io.h> 568c2ecf20Sopenharmony_ci#include <asm/sections.h> 578c2ecf20Sopenharmony_ci#include <asm/irq.h> 588c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 598c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 608c2ecf20Sopenharmony_ci#include <asm/pmac_pfunc.h> 618c2ecf20Sopenharmony_ci#include <asm/pmac_low_i2c.h> 628c2ecf20Sopenharmony_ci#include <asm/prom.h> 638c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 648c2ecf20Sopenharmony_ci#include <asm/cputable.h> 658c2ecf20Sopenharmony_ci#include <asm/time.h> 668c2ecf20Sopenharmony_ci#include <asm/backlight.h> 678c2ecf20Sopenharmony_ci#else 688c2ecf20Sopenharmony_ci#include <asm/macintosh.h> 698c2ecf20Sopenharmony_ci#include <asm/macints.h> 708c2ecf20Sopenharmony_ci#include <asm/mac_via.h> 718c2ecf20Sopenharmony_ci#endif 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#include "via-pmu-event.h" 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Some compile options */ 768c2ecf20Sopenharmony_ci#undef DEBUG_SLEEP 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* How many iterations between battery polls */ 798c2ecf20Sopenharmony_ci#define BATTERY_POLLING_COUNT 2 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pmu_info_proc_mutex); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* VIA registers - spaced 0x200 bytes apart */ 848c2ecf20Sopenharmony_ci#define RS 0x200 /* skip between registers */ 858c2ecf20Sopenharmony_ci#define B 0 /* B-side data */ 868c2ecf20Sopenharmony_ci#define A RS /* A-side data */ 878c2ecf20Sopenharmony_ci#define DIRB (2*RS) /* B-side direction (1=output) */ 888c2ecf20Sopenharmony_ci#define DIRA (3*RS) /* A-side direction (1=output) */ 898c2ecf20Sopenharmony_ci#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ 908c2ecf20Sopenharmony_ci#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ 918c2ecf20Sopenharmony_ci#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ 928c2ecf20Sopenharmony_ci#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ 938c2ecf20Sopenharmony_ci#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ 948c2ecf20Sopenharmony_ci#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ 958c2ecf20Sopenharmony_ci#define SR (10*RS) /* Shift register */ 968c2ecf20Sopenharmony_ci#define ACR (11*RS) /* Auxiliary control register */ 978c2ecf20Sopenharmony_ci#define PCR (12*RS) /* Peripheral control register */ 988c2ecf20Sopenharmony_ci#define IFR (13*RS) /* Interrupt flag register */ 998c2ecf20Sopenharmony_ci#define IER (14*RS) /* Interrupt enable register */ 1008c2ecf20Sopenharmony_ci#define ANH (15*RS) /* A-side data, no handshake */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Bits in B data register: both active low */ 1038c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 1048c2ecf20Sopenharmony_ci#define TACK 0x08 /* Transfer acknowledge (input) */ 1058c2ecf20Sopenharmony_ci#define TREQ 0x10 /* Transfer request (output) */ 1068c2ecf20Sopenharmony_ci#else 1078c2ecf20Sopenharmony_ci#define TACK 0x02 1088c2ecf20Sopenharmony_ci#define TREQ 0x04 1098c2ecf20Sopenharmony_ci#endif 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Bits in ACR */ 1128c2ecf20Sopenharmony_ci#define SR_CTRL 0x1c /* Shift register control bits */ 1138c2ecf20Sopenharmony_ci#define SR_EXT 0x0c /* Shift on external clock */ 1148c2ecf20Sopenharmony_ci#define SR_OUT 0x10 /* Shift out if 1 */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Bits in IFR and IER */ 1178c2ecf20Sopenharmony_ci#define IER_SET 0x80 /* set bits in IER */ 1188c2ecf20Sopenharmony_ci#define IER_CLR 0 /* clear bits in IER */ 1198c2ecf20Sopenharmony_ci#define SR_INT 0x04 /* Shift register full/empty */ 1208c2ecf20Sopenharmony_ci#define CB2_INT 0x08 1218c2ecf20Sopenharmony_ci#define CB1_INT 0x10 /* transition on CB1 input */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic volatile enum pmu_state { 1248c2ecf20Sopenharmony_ci uninitialized = 0, 1258c2ecf20Sopenharmony_ci idle, 1268c2ecf20Sopenharmony_ci sending, 1278c2ecf20Sopenharmony_ci intack, 1288c2ecf20Sopenharmony_ci reading, 1298c2ecf20Sopenharmony_ci reading_intr, 1308c2ecf20Sopenharmony_ci locked, 1318c2ecf20Sopenharmony_ci} pmu_state; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic volatile enum int_data_state { 1348c2ecf20Sopenharmony_ci int_data_empty, 1358c2ecf20Sopenharmony_ci int_data_fill, 1368c2ecf20Sopenharmony_ci int_data_ready, 1378c2ecf20Sopenharmony_ci int_data_flush 1388c2ecf20Sopenharmony_ci} int_data_state[2] = { int_data_empty, int_data_empty }; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct adb_request *current_req; 1418c2ecf20Sopenharmony_cistatic struct adb_request *last_req; 1428c2ecf20Sopenharmony_cistatic struct adb_request *req_awaiting_reply; 1438c2ecf20Sopenharmony_cistatic unsigned char interrupt_data[2][32]; 1448c2ecf20Sopenharmony_cistatic int interrupt_data_len[2]; 1458c2ecf20Sopenharmony_cistatic int int_data_last; 1468c2ecf20Sopenharmony_cistatic unsigned char *reply_ptr; 1478c2ecf20Sopenharmony_cistatic int data_index; 1488c2ecf20Sopenharmony_cistatic int data_len; 1498c2ecf20Sopenharmony_cistatic volatile int adb_int_pending; 1508c2ecf20Sopenharmony_cistatic volatile int disable_poll; 1518c2ecf20Sopenharmony_cistatic int pmu_kind = PMU_UNKNOWN; 1528c2ecf20Sopenharmony_cistatic int pmu_fully_inited; 1538c2ecf20Sopenharmony_cistatic int pmu_has_adb; 1548c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 1558c2ecf20Sopenharmony_cistatic volatile unsigned char __iomem *via1; 1568c2ecf20Sopenharmony_cistatic volatile unsigned char __iomem *via2; 1578c2ecf20Sopenharmony_cistatic struct device_node *vias; 1588c2ecf20Sopenharmony_cistatic struct device_node *gpio_node; 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_cistatic unsigned char __iomem *gpio_reg; 1618c2ecf20Sopenharmony_cistatic int gpio_irq = 0; 1628c2ecf20Sopenharmony_cistatic int gpio_irq_enabled = -1; 1638c2ecf20Sopenharmony_cistatic volatile int pmu_suspended; 1648c2ecf20Sopenharmony_cistatic spinlock_t pmu_lock; 1658c2ecf20Sopenharmony_cistatic u8 pmu_intr_mask; 1668c2ecf20Sopenharmony_cistatic int pmu_version; 1678c2ecf20Sopenharmony_cistatic int drop_interrupts; 1688c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 1698c2ecf20Sopenharmony_cistatic int option_lid_wakeup = 1; 1708c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 1718c2ecf20Sopenharmony_cistatic unsigned long async_req_locks; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#define NUM_IRQ_STATS 13 1748c2ecf20Sopenharmony_cistatic unsigned int pmu_irq_stats[NUM_IRQ_STATS]; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_root; 1778c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_info; 1788c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_irqstats; 1798c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_options; 1808c2ecf20Sopenharmony_cistatic int option_server_mode; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciint pmu_battery_count; 1838c2ecf20Sopenharmony_ciint pmu_cur_battery; 1848c2ecf20Sopenharmony_ciunsigned int pmu_power_flags = PMU_PWR_AC_PRESENT; 1858c2ecf20Sopenharmony_cistruct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; 1868c2ecf20Sopenharmony_cistatic int query_batt_timer = BATTERY_POLLING_COUNT; 1878c2ecf20Sopenharmony_cistatic struct adb_request batt_req; 1888c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES]; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciint __fake_sleep; 1918c2ecf20Sopenharmony_ciint asleep; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 1948c2ecf20Sopenharmony_cistatic int adb_dev_map; 1958c2ecf20Sopenharmony_cistatic int pmu_adb_flags; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int pmu_probe(void); 1988c2ecf20Sopenharmony_cistatic int pmu_init(void); 1998c2ecf20Sopenharmony_cistatic int pmu_send_request(struct adb_request *req, int sync); 2008c2ecf20Sopenharmony_cistatic int pmu_adb_autopoll(int devs); 2018c2ecf20Sopenharmony_cistatic int pmu_adb_reset_bus(void); 2028c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int init_pmu(void); 2058c2ecf20Sopenharmony_cistatic void pmu_start(void); 2068c2ecf20Sopenharmony_cistatic irqreturn_t via_pmu_interrupt(int irq, void *arg); 2078c2ecf20Sopenharmony_cistatic irqreturn_t gpio1_interrupt(int irq, void *arg); 2088c2ecf20Sopenharmony_cistatic int pmu_info_proc_show(struct seq_file *m, void *v); 2098c2ecf20Sopenharmony_cistatic int pmu_irqstats_proc_show(struct seq_file *m, void *v); 2108c2ecf20Sopenharmony_cistatic int pmu_battery_proc_show(struct seq_file *m, void *v); 2118c2ecf20Sopenharmony_cistatic void pmu_pass_intr(unsigned char *data, int len); 2128c2ecf20Sopenharmony_cistatic const struct proc_ops pmu_options_proc_ops; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 2158c2ecf20Sopenharmony_ciconst struct adb_driver via_pmu_driver = { 2168c2ecf20Sopenharmony_ci .name = "PMU", 2178c2ecf20Sopenharmony_ci .probe = pmu_probe, 2188c2ecf20Sopenharmony_ci .init = pmu_init, 2198c2ecf20Sopenharmony_ci .send_request = pmu_send_request, 2208c2ecf20Sopenharmony_ci .autopoll = pmu_adb_autopoll, 2218c2ecf20Sopenharmony_ci .poll = pmu_poll_adb, 2228c2ecf20Sopenharmony_ci .reset_bus = pmu_adb_reset_bus, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciextern void low_sleep_handler(void); 2278c2ecf20Sopenharmony_ciextern void enable_kernel_altivec(void); 2288c2ecf20Sopenharmony_ciextern void enable_kernel_fp(void); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci#ifdef DEBUG_SLEEP 2318c2ecf20Sopenharmony_ciint pmu_polled_request(struct adb_request *req); 2328c2ecf20Sopenharmony_civoid pmu_blink(int n); 2338c2ecf20Sopenharmony_ci#endif 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * This table indicates for each PMU opcode: 2378c2ecf20Sopenharmony_ci * - the number of data bytes to be sent with the command, or -1 2388c2ecf20Sopenharmony_ci * if a length byte should be sent, 2398c2ecf20Sopenharmony_ci * - the number of response bytes which the PMU will return, or 2408c2ecf20Sopenharmony_ci * -1 if it will send a length byte. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic const s8 pmu_data_len[256][2] = { 2438c2ecf20Sopenharmony_ci/* 0 1 2 3 4 5 6 7 */ 2448c2ecf20Sopenharmony_ci/*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2458c2ecf20Sopenharmony_ci/*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2468c2ecf20Sopenharmony_ci/*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2478c2ecf20Sopenharmony_ci/*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0}, 2488c2ecf20Sopenharmony_ci/*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, 2498c2ecf20Sopenharmony_ci/*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1}, 2508c2ecf20Sopenharmony_ci/*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2518c2ecf20Sopenharmony_ci/*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0}, 2528c2ecf20Sopenharmony_ci/*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2538c2ecf20Sopenharmony_ci/*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1}, 2548c2ecf20Sopenharmony_ci/*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, 2558c2ecf20Sopenharmony_ci/*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1}, 2568c2ecf20Sopenharmony_ci/*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2578c2ecf20Sopenharmony_ci/*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1}, 2588c2ecf20Sopenharmony_ci/*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2598c2ecf20Sopenharmony_ci/*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1}, 2608c2ecf20Sopenharmony_ci/*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2618c2ecf20Sopenharmony_ci/*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2628c2ecf20Sopenharmony_ci/*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2638c2ecf20Sopenharmony_ci/*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2648c2ecf20Sopenharmony_ci/*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, 2658c2ecf20Sopenharmony_ci/*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2668c2ecf20Sopenharmony_ci/*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2678c2ecf20Sopenharmony_ci/*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2688c2ecf20Sopenharmony_ci/*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2698c2ecf20Sopenharmony_ci/*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2708c2ecf20Sopenharmony_ci/*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2718c2ecf20Sopenharmony_ci/*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1}, 2728c2ecf20Sopenharmony_ci/*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, 2738c2ecf20Sopenharmony_ci/*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0}, 2748c2ecf20Sopenharmony_ci/*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 2758c2ecf20Sopenharmony_ci/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic char *pbook_type[] = { 2798c2ecf20Sopenharmony_ci "Unknown PowerBook", 2808c2ecf20Sopenharmony_ci "PowerBook 2400/3400/3500(G3)", 2818c2ecf20Sopenharmony_ci "PowerBook G3 Series", 2828c2ecf20Sopenharmony_ci "1999 PowerBook G3", 2838c2ecf20Sopenharmony_ci "Core99" 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciint __init find_via_pmu(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 2898c2ecf20Sopenharmony_ci u64 taddr; 2908c2ecf20Sopenharmony_ci const u32 *reg; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (pmu_state != uninitialized) 2938c2ecf20Sopenharmony_ci return 1; 2948c2ecf20Sopenharmony_ci vias = of_find_node_by_name(NULL, "via-pmu"); 2958c2ecf20Sopenharmony_ci if (vias == NULL) 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci reg = of_get_property(vias, "reg", NULL); 2998c2ecf20Sopenharmony_ci if (reg == NULL) { 3008c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); 3018c2ecf20Sopenharmony_ci goto fail; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci taddr = of_translate_address(vias, reg); 3048c2ecf20Sopenharmony_ci if (taddr == OF_BAD_ADDR) { 3058c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: Can't translate address !\n"); 3068c2ecf20Sopenharmony_ci goto fail; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_lock_init(&pmu_lock); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pmu_has_adb = 1; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 3148c2ecf20Sopenharmony_ci PMU_INT_SNDBRT | 3158c2ecf20Sopenharmony_ci PMU_INT_ADB | 3168c2ecf20Sopenharmony_ci PMU_INT_TICK; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (of_node_name_eq(vias->parent, "ohare") || 3198c2ecf20Sopenharmony_ci of_device_is_compatible(vias->parent, "ohare")) 3208c2ecf20Sopenharmony_ci pmu_kind = PMU_OHARE_BASED; 3218c2ecf20Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "paddington")) 3228c2ecf20Sopenharmony_ci pmu_kind = PMU_PADDINGTON_BASED; 3238c2ecf20Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "heathrow")) 3248c2ecf20Sopenharmony_ci pmu_kind = PMU_HEATHROW_BASED; 3258c2ecf20Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "Keylargo") 3268c2ecf20Sopenharmony_ci || of_device_is_compatible(vias->parent, "K2-Keylargo")) { 3278c2ecf20Sopenharmony_ci struct device_node *gpiop; 3288c2ecf20Sopenharmony_ci struct device_node *adbp; 3298c2ecf20Sopenharmony_ci u64 gaddr = OF_BAD_ADDR; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pmu_kind = PMU_KEYLARGO_BASED; 3328c2ecf20Sopenharmony_ci adbp = of_find_node_by_type(NULL, "adb"); 3338c2ecf20Sopenharmony_ci pmu_has_adb = (adbp != NULL); 3348c2ecf20Sopenharmony_ci of_node_put(adbp); 3358c2ecf20Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 3368c2ecf20Sopenharmony_ci PMU_INT_SNDBRT | 3378c2ecf20Sopenharmony_ci PMU_INT_ADB | 3388c2ecf20Sopenharmony_ci PMU_INT_TICK | 3398c2ecf20Sopenharmony_ci PMU_INT_ENVIRONMENT; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci gpiop = of_find_node_by_name(NULL, "gpio"); 3428c2ecf20Sopenharmony_ci if (gpiop) { 3438c2ecf20Sopenharmony_ci reg = of_get_property(gpiop, "reg", NULL); 3448c2ecf20Sopenharmony_ci if (reg) 3458c2ecf20Sopenharmony_ci gaddr = of_translate_address(gpiop, reg); 3468c2ecf20Sopenharmony_ci if (gaddr != OF_BAD_ADDR) 3478c2ecf20Sopenharmony_ci gpio_reg = ioremap(gaddr, 0x10); 3488c2ecf20Sopenharmony_ci of_node_put(gpiop); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (gpio_reg == NULL) { 3518c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); 3528c2ecf20Sopenharmony_ci goto fail; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } else 3558c2ecf20Sopenharmony_ci pmu_kind = PMU_UNKNOWN; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci via1 = via2 = ioremap(taddr, 0x2000); 3588c2ecf20Sopenharmony_ci if (via1 == NULL) { 3598c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: Can't map address !\n"); 3608c2ecf20Sopenharmony_ci goto fail_via_remap; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci out_8(&via1[IER], IER_CLR | 0x7f); /* disable all intrs */ 3648c2ecf20Sopenharmony_ci out_8(&via1[IFR], 0x7f); /* clear IFR */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pmu_state = idle; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!init_pmu()) 3698c2ecf20Sopenharmony_ci goto fail_init; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci sys_ctrler = SYS_CTRLER_PMU; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 1; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci fail_init: 3768c2ecf20Sopenharmony_ci iounmap(via1); 3778c2ecf20Sopenharmony_ci via1 = via2 = NULL; 3788c2ecf20Sopenharmony_ci fail_via_remap: 3798c2ecf20Sopenharmony_ci iounmap(gpio_reg); 3808c2ecf20Sopenharmony_ci gpio_reg = NULL; 3818c2ecf20Sopenharmony_ci fail: 3828c2ecf20Sopenharmony_ci of_node_put(vias); 3838c2ecf20Sopenharmony_ci vias = NULL; 3848c2ecf20Sopenharmony_ci pmu_state = uninitialized; 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci#else 3878c2ecf20Sopenharmony_ci if (macintosh_config->adb_type != MAC_ADB_PB2) 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci pmu_kind = PMU_UNKNOWN; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci spin_lock_init(&pmu_lock); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci pmu_has_adb = 1; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 3978c2ecf20Sopenharmony_ci PMU_INT_SNDBRT | 3988c2ecf20Sopenharmony_ci PMU_INT_ADB | 3998c2ecf20Sopenharmony_ci PMU_INT_TICK; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pmu_state = idle; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!init_pmu()) { 4048c2ecf20Sopenharmony_ci pmu_state = uninitialized; 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 1; 4098c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC_PMAC */ 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 4138c2ecf20Sopenharmony_cistatic int pmu_probe(void) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci return pmu_state == uninitialized ? -ENODEV : 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int pmu_init(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci return pmu_state == uninitialized ? -ENODEV : 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* 4258c2ecf20Sopenharmony_ci * We can't wait until pmu_init gets called, that happens too late. 4268c2ecf20Sopenharmony_ci * It happens after IDE and SCSI initialization, which can take a few 4278c2ecf20Sopenharmony_ci * seconds, and by that time the PMU could have given up on us and 4288c2ecf20Sopenharmony_ci * turned us off. 4298c2ecf20Sopenharmony_ci * Thus this is called with arch_initcall rather than device_initcall. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_cistatic int __init via_pmu_start(void) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci unsigned int __maybe_unused irq; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 4368c2ecf20Sopenharmony_ci return -ENODEV; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci batt_req.complete = 1; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 4418c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(vias, 0); 4428c2ecf20Sopenharmony_ci if (!irq) { 4438c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: can't map interrupt\n"); 4448c2ecf20Sopenharmony_ci return -ENODEV; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci /* We set IRQF_NO_SUSPEND because we don't want the interrupt 4478c2ecf20Sopenharmony_ci * to be disabled between the 2 passes of driver suspend, we 4488c2ecf20Sopenharmony_ci * control our own disabling for that one 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci if (request_irq(irq, via_pmu_interrupt, IRQF_NO_SUSPEND, 4518c2ecf20Sopenharmony_ci "VIA-PMU", (void *)0)) { 4528c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: can't request irq %d\n", irq); 4538c2ecf20Sopenharmony_ci return -ENODEV; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 4578c2ecf20Sopenharmony_ci gpio_node = of_find_node_by_name(NULL, "extint-gpio1"); 4588c2ecf20Sopenharmony_ci if (gpio_node == NULL) 4598c2ecf20Sopenharmony_ci gpio_node = of_find_node_by_name(NULL, 4608c2ecf20Sopenharmony_ci "pmu-interrupt"); 4618c2ecf20Sopenharmony_ci if (gpio_node) 4628c2ecf20Sopenharmony_ci gpio_irq = irq_of_parse_and_map(gpio_node, 0); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (gpio_irq) { 4658c2ecf20Sopenharmony_ci if (request_irq(gpio_irq, gpio1_interrupt, 4668c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND, "GPIO1 ADB", 4678c2ecf20Sopenharmony_ci (void *)0)) 4688c2ecf20Sopenharmony_ci printk(KERN_ERR "pmu: can't get irq %d" 4698c2ecf20Sopenharmony_ci " (GPIO1)\n", gpio_irq); 4708c2ecf20Sopenharmony_ci else 4718c2ecf20Sopenharmony_ci gpio_irq_enabled = 1; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Enable interrupts */ 4768c2ecf20Sopenharmony_ci out_8(&via1[IER], IER_SET | SR_INT | CB1_INT); 4778c2ecf20Sopenharmony_ci#else 4788c2ecf20Sopenharmony_ci if (request_irq(IRQ_MAC_ADB_SR, via_pmu_interrupt, IRQF_NO_SUSPEND, 4798c2ecf20Sopenharmony_ci "VIA-PMU-SR", NULL)) { 4808c2ecf20Sopenharmony_ci pr_err("%s: couldn't get SR irq\n", __func__); 4818c2ecf20Sopenharmony_ci return -ENODEV; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if (request_irq(IRQ_MAC_ADB_CL, via_pmu_interrupt, IRQF_NO_SUSPEND, 4848c2ecf20Sopenharmony_ci "VIA-PMU-CL", NULL)) { 4858c2ecf20Sopenharmony_ci pr_err("%s: couldn't get CL irq\n", __func__); 4868c2ecf20Sopenharmony_ci free_irq(IRQ_MAC_ADB_SR, NULL); 4878c2ecf20Sopenharmony_ci return -ENODEV; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC_PMAC */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci pmu_fully_inited = 1; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Make sure PMU settle down before continuing. This is _very_ important 4948c2ecf20Sopenharmony_ci * since the IDE probe may shut interrupts down for quite a bit of time. If 4958c2ecf20Sopenharmony_ci * a PMU communication is pending while this happens, the PMU may timeout 4968c2ecf20Sopenharmony_ci * Not that on Core99 machines, the PMU keeps sending us environement 4978c2ecf20Sopenharmony_ci * messages, we should find a way to either fix IDE or make it call 4988c2ecf20Sopenharmony_ci * pmu_suspend() before masking interrupts. This can also happens while 4998c2ecf20Sopenharmony_ci * scolling with some fbdevs. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci do { 5028c2ecf20Sopenharmony_ci pmu_poll(); 5038c2ecf20Sopenharmony_ci } while (pmu_state != idle); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciarch_initcall(via_pmu_start); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * This has to be done after pci_init, which is a subsys_initcall. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic int __init via_pmu_dev_init(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 5168c2ecf20Sopenharmony_ci return -ENODEV; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 5198c2ecf20Sopenharmony_ci /* Initialize backlight */ 5208c2ecf20Sopenharmony_ci pmu_backlight_init(); 5218c2ecf20Sopenharmony_ci#endif 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 5248c2ecf20Sopenharmony_ci if (of_machine_is_compatible("AAPL,3400/2400") || 5258c2ecf20Sopenharmony_ci of_machine_is_compatible("AAPL,3500")) { 5268c2ecf20Sopenharmony_ci int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, 5278c2ecf20Sopenharmony_ci NULL, PMAC_MB_INFO_MODEL, 0); 5288c2ecf20Sopenharmony_ci pmu_battery_count = 1; 5298c2ecf20Sopenharmony_ci if (mb == PMAC_TYPE_COMET) 5308c2ecf20Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET; 5318c2ecf20Sopenharmony_ci else 5328c2ecf20Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER; 5338c2ecf20Sopenharmony_ci } else if (of_machine_is_compatible("AAPL,PowerBook1998") || 5348c2ecf20Sopenharmony_ci of_machine_is_compatible("PowerBook1,1")) { 5358c2ecf20Sopenharmony_ci pmu_battery_count = 2; 5368c2ecf20Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; 5378c2ecf20Sopenharmony_ci pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci struct device_node* prim = 5408c2ecf20Sopenharmony_ci of_find_node_by_name(NULL, "power-mgt"); 5418c2ecf20Sopenharmony_ci const u32 *prim_info = NULL; 5428c2ecf20Sopenharmony_ci if (prim) 5438c2ecf20Sopenharmony_ci prim_info = of_get_property(prim, "prim-info", NULL); 5448c2ecf20Sopenharmony_ci if (prim_info) { 5458c2ecf20Sopenharmony_ci /* Other stuffs here yet unknown */ 5468c2ecf20Sopenharmony_ci pmu_battery_count = (prim_info[6] >> 16) & 0xff; 5478c2ecf20Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; 5488c2ecf20Sopenharmony_ci if (pmu_battery_count > 1) 5498c2ecf20Sopenharmony_ci pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci of_node_put(prim); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC32 */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* Create /proc/pmu */ 5568c2ecf20Sopenharmony_ci proc_pmu_root = proc_mkdir("pmu", NULL); 5578c2ecf20Sopenharmony_ci if (proc_pmu_root) { 5588c2ecf20Sopenharmony_ci long i; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci for (i=0; i<pmu_battery_count; i++) { 5618c2ecf20Sopenharmony_ci char title[16]; 5628c2ecf20Sopenharmony_ci sprintf(title, "battery_%ld", i); 5638c2ecf20Sopenharmony_ci proc_pmu_batt[i] = proc_create_single_data(title, 0, 5648c2ecf20Sopenharmony_ci proc_pmu_root, pmu_battery_proc_show, 5658c2ecf20Sopenharmony_ci (void *)i); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci proc_pmu_info = proc_create_single("info", 0, proc_pmu_root, 5698c2ecf20Sopenharmony_ci pmu_info_proc_show); 5708c2ecf20Sopenharmony_ci proc_pmu_irqstats = proc_create_single("interrupts", 0, 5718c2ecf20Sopenharmony_ci proc_pmu_root, pmu_irqstats_proc_show); 5728c2ecf20Sopenharmony_ci proc_pmu_options = proc_create("options", 0600, proc_pmu_root, 5738c2ecf20Sopenharmony_ci &pmu_options_proc_ops); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cidevice_initcall(via_pmu_dev_init); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int 5818c2ecf20Sopenharmony_ciinit_pmu(void) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci int timeout; 5848c2ecf20Sopenharmony_ci struct adb_request req; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Negate TREQ. Set TACK to input and TREQ to output. */ 5878c2ecf20Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) | TREQ); 5888c2ecf20Sopenharmony_ci out_8(&via2[DIRB], (in_8(&via2[DIRB]) | TREQ) & ~TACK); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 5918c2ecf20Sopenharmony_ci timeout = 100000; 5928c2ecf20Sopenharmony_ci while (!req.complete) { 5938c2ecf20Sopenharmony_ci if (--timeout < 0) { 5948c2ecf20Sopenharmony_ci printk(KERN_ERR "init_pmu: no response from PMU\n"); 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci udelay(10); 5988c2ecf20Sopenharmony_ci pmu_poll(); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* ack all pending interrupts */ 6028c2ecf20Sopenharmony_ci timeout = 100000; 6038c2ecf20Sopenharmony_ci interrupt_data[0][0] = 1; 6048c2ecf20Sopenharmony_ci while (interrupt_data[0][0] || pmu_state != idle) { 6058c2ecf20Sopenharmony_ci if (--timeout < 0) { 6068c2ecf20Sopenharmony_ci printk(KERN_ERR "init_pmu: timed out acking intrs\n"); 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci if (pmu_state == idle) 6108c2ecf20Sopenharmony_ci adb_int_pending = 1; 6118c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 6128c2ecf20Sopenharmony_ci udelay(10); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Tell PMU we are ready. */ 6168c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 6178c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 6188c2ecf20Sopenharmony_ci while (!req.complete) 6198c2ecf20Sopenharmony_ci pmu_poll(); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* Read PMU version */ 6238c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_GET_VERSION); 6248c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 6258c2ecf20Sopenharmony_ci if (req.reply_len > 0) 6268c2ecf20Sopenharmony_ci pmu_version = req.reply[0]; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Read server mode setting */ 6298c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 6308c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, 6318c2ecf20Sopenharmony_ci PMU_PWR_GET_POWERUP_EVENTS); 6328c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 6338c2ecf20Sopenharmony_ci if (req.reply_len == 2) { 6348c2ecf20Sopenharmony_ci if (req.reply[1] & PMU_PWR_WAKEUP_AC_INSERT) 6358c2ecf20Sopenharmony_ci option_server_mode = 1; 6368c2ecf20Sopenharmony_ci printk(KERN_INFO "via-pmu: Server Mode is %s\n", 6378c2ecf20Sopenharmony_ci option_server_mode ? "enabled" : "disabled"); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci printk(KERN_INFO "PMU driver v%d initialized for %s, firmware: %02x\n", 6428c2ecf20Sopenharmony_ci PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 1; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ciint 6488c2ecf20Sopenharmony_cipmu_get_model(void) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci return pmu_kind; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void pmu_set_server_mode(int server_mode) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct adb_request req; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) 6588c2ecf20Sopenharmony_ci return; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci option_server_mode = server_mode; 6618c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, PMU_PWR_GET_POWERUP_EVENTS); 6628c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 6638c2ecf20Sopenharmony_ci if (req.reply_len < 2) 6648c2ecf20Sopenharmony_ci return; 6658c2ecf20Sopenharmony_ci if (server_mode) 6668c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, 6678c2ecf20Sopenharmony_ci PMU_PWR_SET_POWERUP_EVENTS, 6688c2ecf20Sopenharmony_ci req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, 6718c2ecf20Sopenharmony_ci PMU_PWR_CLR_POWERUP_EVENTS, 6728c2ecf20Sopenharmony_ci req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 6738c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/* This new version of the code for 2400/3400/3500 powerbooks 6778c2ecf20Sopenharmony_ci * is inspired from the implementation in gkrellm-pmu 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_cistatic void 6808c2ecf20Sopenharmony_cidone_battery_state_ohare(struct adb_request* req) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 6838c2ecf20Sopenharmony_ci /* format: 6848c2ecf20Sopenharmony_ci * [0] : flags 6858c2ecf20Sopenharmony_ci * 0x01 : AC indicator 6868c2ecf20Sopenharmony_ci * 0x02 : charging 6878c2ecf20Sopenharmony_ci * 0x04 : battery exist 6888c2ecf20Sopenharmony_ci * 0x08 : 6898c2ecf20Sopenharmony_ci * 0x10 : 6908c2ecf20Sopenharmony_ci * 0x20 : full charged 6918c2ecf20Sopenharmony_ci * 0x40 : pcharge reset 6928c2ecf20Sopenharmony_ci * 0x80 : battery exist 6938c2ecf20Sopenharmony_ci * 6948c2ecf20Sopenharmony_ci * [1][2] : battery voltage 6958c2ecf20Sopenharmony_ci * [3] : CPU temperature 6968c2ecf20Sopenharmony_ci * [4] : battery temperature 6978c2ecf20Sopenharmony_ci * [5] : current 6988c2ecf20Sopenharmony_ci * [6][7] : pcharge 6998c2ecf20Sopenharmony_ci * --tkoba 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci unsigned int bat_flags = PMU_BATT_TYPE_HOOPER; 7028c2ecf20Sopenharmony_ci long pcharge, charge, vb, vmax, lmax; 7038c2ecf20Sopenharmony_ci long vmax_charging, vmax_charged; 7048c2ecf20Sopenharmony_ci long amperage, voltage, time, max; 7058c2ecf20Sopenharmony_ci int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, 7068c2ecf20Sopenharmony_ci NULL, PMAC_MB_INFO_MODEL, 0); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (req->reply[0] & 0x01) 7098c2ecf20Sopenharmony_ci pmu_power_flags |= PMU_PWR_AC_PRESENT; 7108c2ecf20Sopenharmony_ci else 7118c2ecf20Sopenharmony_ci pmu_power_flags &= ~PMU_PWR_AC_PRESENT; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (mb == PMAC_TYPE_COMET) { 7148c2ecf20Sopenharmony_ci vmax_charged = 189; 7158c2ecf20Sopenharmony_ci vmax_charging = 213; 7168c2ecf20Sopenharmony_ci lmax = 6500; 7178c2ecf20Sopenharmony_ci } else { 7188c2ecf20Sopenharmony_ci vmax_charged = 330; 7198c2ecf20Sopenharmony_ci vmax_charging = 330; 7208c2ecf20Sopenharmony_ci lmax = 6500; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci vmax = vmax_charged; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* If battery installed */ 7258c2ecf20Sopenharmony_ci if (req->reply[0] & 0x04) { 7268c2ecf20Sopenharmony_ci bat_flags |= PMU_BATT_PRESENT; 7278c2ecf20Sopenharmony_ci if (req->reply[0] & 0x02) 7288c2ecf20Sopenharmony_ci bat_flags |= PMU_BATT_CHARGING; 7298c2ecf20Sopenharmony_ci vb = (req->reply[1] << 8) | req->reply[2]; 7308c2ecf20Sopenharmony_ci voltage = (vb * 265 + 72665) / 10; 7318c2ecf20Sopenharmony_ci amperage = req->reply[5]; 7328c2ecf20Sopenharmony_ci if ((req->reply[0] & 0x01) == 0) { 7338c2ecf20Sopenharmony_ci if (amperage > 200) 7348c2ecf20Sopenharmony_ci vb += ((amperage - 200) * 15)/100; 7358c2ecf20Sopenharmony_ci } else if (req->reply[0] & 0x02) { 7368c2ecf20Sopenharmony_ci vb = (vb * 97) / 100; 7378c2ecf20Sopenharmony_ci vmax = vmax_charging; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci charge = (100 * vb) / vmax; 7408c2ecf20Sopenharmony_ci if (req->reply[0] & 0x40) { 7418c2ecf20Sopenharmony_ci pcharge = (req->reply[6] << 8) + req->reply[7]; 7428c2ecf20Sopenharmony_ci if (pcharge > lmax) 7438c2ecf20Sopenharmony_ci pcharge = lmax; 7448c2ecf20Sopenharmony_ci pcharge *= 100; 7458c2ecf20Sopenharmony_ci pcharge = 100 - pcharge / lmax; 7468c2ecf20Sopenharmony_ci if (pcharge < charge) 7478c2ecf20Sopenharmony_ci charge = pcharge; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci if (amperage > 0) 7508c2ecf20Sopenharmony_ci time = (charge * 16440) / amperage; 7518c2ecf20Sopenharmony_ci else 7528c2ecf20Sopenharmony_ci time = 0; 7538c2ecf20Sopenharmony_ci max = 100; 7548c2ecf20Sopenharmony_ci amperage = -amperage; 7558c2ecf20Sopenharmony_ci } else 7568c2ecf20Sopenharmony_ci charge = max = amperage = voltage = time = 0; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].flags = bat_flags; 7598c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].charge = charge; 7608c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].max_charge = max; 7618c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].amperage = amperage; 7628c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].voltage = voltage; 7638c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining = time; 7648c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci clear_bit(0, &async_req_locks); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic void 7708c2ecf20Sopenharmony_cidone_battery_state_smart(struct adb_request* req) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci /* format: 7738c2ecf20Sopenharmony_ci * [0] : format of this structure (known: 3,4,5) 7748c2ecf20Sopenharmony_ci * [1] : flags 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * format 3 & 4: 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * [2] : charge 7798c2ecf20Sopenharmony_ci * [3] : max charge 7808c2ecf20Sopenharmony_ci * [4] : current 7818c2ecf20Sopenharmony_ci * [5] : voltage 7828c2ecf20Sopenharmony_ci * 7838c2ecf20Sopenharmony_ci * format 5: 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * [2][3] : charge 7868c2ecf20Sopenharmony_ci * [4][5] : max charge 7878c2ecf20Sopenharmony_ci * [6][7] : current 7888c2ecf20Sopenharmony_ci * [8][9] : voltage 7898c2ecf20Sopenharmony_ci */ 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci unsigned int bat_flags = PMU_BATT_TYPE_SMART; 7928c2ecf20Sopenharmony_ci int amperage; 7938c2ecf20Sopenharmony_ci unsigned int capa, max, voltage; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (req->reply[1] & 0x01) 7968c2ecf20Sopenharmony_ci pmu_power_flags |= PMU_PWR_AC_PRESENT; 7978c2ecf20Sopenharmony_ci else 7988c2ecf20Sopenharmony_ci pmu_power_flags &= ~PMU_PWR_AC_PRESENT; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci capa = max = amperage = voltage = 0; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (req->reply[1] & 0x04) { 8048c2ecf20Sopenharmony_ci bat_flags |= PMU_BATT_PRESENT; 8058c2ecf20Sopenharmony_ci switch(req->reply[0]) { 8068c2ecf20Sopenharmony_ci case 3: 8078c2ecf20Sopenharmony_ci case 4: capa = req->reply[2]; 8088c2ecf20Sopenharmony_ci max = req->reply[3]; 8098c2ecf20Sopenharmony_ci amperage = *((signed char *)&req->reply[4]); 8108c2ecf20Sopenharmony_ci voltage = req->reply[5]; 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci case 5: capa = (req->reply[2] << 8) | req->reply[3]; 8138c2ecf20Sopenharmony_ci max = (req->reply[4] << 8) | req->reply[5]; 8148c2ecf20Sopenharmony_ci amperage = *((signed short *)&req->reply[6]); 8158c2ecf20Sopenharmony_ci voltage = (req->reply[8] << 8) | req->reply[9]; 8168c2ecf20Sopenharmony_ci break; 8178c2ecf20Sopenharmony_ci default: 8188c2ecf20Sopenharmony_ci pr_warn("pmu.c: unrecognized battery info, " 8198c2ecf20Sopenharmony_ci "len: %d, %4ph\n", req->reply_len, 8208c2ecf20Sopenharmony_ci req->reply); 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if ((req->reply[1] & 0x01) && (amperage > 0)) 8268c2ecf20Sopenharmony_ci bat_flags |= PMU_BATT_CHARGING; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].flags = bat_flags; 8298c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].charge = capa; 8308c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].max_charge = max; 8318c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].amperage = amperage; 8328c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].voltage = voltage; 8338c2ecf20Sopenharmony_ci if (amperage) { 8348c2ecf20Sopenharmony_ci if ((req->reply[1] & 0x01) && (amperage > 0)) 8358c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining 8368c2ecf20Sopenharmony_ci = ((max-capa) * 3600) / amperage; 8378c2ecf20Sopenharmony_ci else 8388c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining 8398c2ecf20Sopenharmony_ci = (capa * 3600) / (-amperage); 8408c2ecf20Sopenharmony_ci } else 8418c2ecf20Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining = 0; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci clear_bit(0, &async_req_locks); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic void 8498c2ecf20Sopenharmony_ciquery_battery_state(void) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci if (test_and_set_bit(0, &async_req_locks)) 8528c2ecf20Sopenharmony_ci return; 8538c2ecf20Sopenharmony_ci if (pmu_kind == PMU_OHARE_BASED) 8548c2ecf20Sopenharmony_ci pmu_request(&batt_req, done_battery_state_ohare, 8558c2ecf20Sopenharmony_ci 1, PMU_BATTERY_STATE); 8568c2ecf20Sopenharmony_ci else 8578c2ecf20Sopenharmony_ci pmu_request(&batt_req, done_battery_state_smart, 8588c2ecf20Sopenharmony_ci 2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic int pmu_info_proc_show(struct seq_file *m, void *v) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci seq_printf(m, "PMU driver version : %d\n", PMU_DRIVER_VERSION); 8648c2ecf20Sopenharmony_ci seq_printf(m, "PMU firmware version : %02x\n", pmu_version); 8658c2ecf20Sopenharmony_ci seq_printf(m, "AC Power : %d\n", 8668c2ecf20Sopenharmony_ci ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0) || pmu_battery_count == 0); 8678c2ecf20Sopenharmony_ci seq_printf(m, "Battery count : %d\n", pmu_battery_count); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int pmu_irqstats_proc_show(struct seq_file *m, void *v) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci int i; 8758c2ecf20Sopenharmony_ci static const char *irq_names[NUM_IRQ_STATS] = { 8768c2ecf20Sopenharmony_ci "Unknown interrupt (type 0)", 8778c2ecf20Sopenharmony_ci "Unknown interrupt (type 1)", 8788c2ecf20Sopenharmony_ci "PC-Card eject button", 8798c2ecf20Sopenharmony_ci "Sound/Brightness button", 8808c2ecf20Sopenharmony_ci "ADB message", 8818c2ecf20Sopenharmony_ci "Battery state change", 8828c2ecf20Sopenharmony_ci "Environment interrupt", 8838c2ecf20Sopenharmony_ci "Tick timer", 8848c2ecf20Sopenharmony_ci "Ghost interrupt (zero len)", 8858c2ecf20Sopenharmony_ci "Empty interrupt (empty mask)", 8868c2ecf20Sopenharmony_ci "Max irqs in a row", 8878c2ecf20Sopenharmony_ci "Total CB1 triggered events", 8888c2ecf20Sopenharmony_ci "Total GPIO1 triggered events", 8898c2ecf20Sopenharmony_ci }; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci for (i = 0; i < NUM_IRQ_STATS; i++) { 8928c2ecf20Sopenharmony_ci seq_printf(m, " %2u: %10u (%s)\n", 8938c2ecf20Sopenharmony_ci i, pmu_irq_stats[i], irq_names[i]); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic int pmu_battery_proc_show(struct seq_file *m, void *v) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci long batnum = (long)m->private; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 9038c2ecf20Sopenharmony_ci seq_printf(m, "flags : %08x\n", pmu_batteries[batnum].flags); 9048c2ecf20Sopenharmony_ci seq_printf(m, "charge : %d\n", pmu_batteries[batnum].charge); 9058c2ecf20Sopenharmony_ci seq_printf(m, "max_charge : %d\n", pmu_batteries[batnum].max_charge); 9068c2ecf20Sopenharmony_ci seq_printf(m, "current : %d\n", pmu_batteries[batnum].amperage); 9078c2ecf20Sopenharmony_ci seq_printf(m, "voltage : %d\n", pmu_batteries[batnum].voltage); 9088c2ecf20Sopenharmony_ci seq_printf(m, "time rem. : %d\n", pmu_batteries[batnum].time_remaining); 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic int pmu_options_proc_show(struct seq_file *m, void *v) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 9158c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && 9168c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) 9178c2ecf20Sopenharmony_ci seq_printf(m, "lid_wakeup=%d\n", option_lid_wakeup); 9188c2ecf20Sopenharmony_ci#endif 9198c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) 9208c2ecf20Sopenharmony_ci seq_printf(m, "server_mode=%d\n", option_server_mode); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return 0; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic int pmu_options_proc_open(struct inode *inode, struct file *file) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci return single_open(file, pmu_options_proc_show, NULL); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic ssize_t pmu_options_proc_write(struct file *file, 9318c2ecf20Sopenharmony_ci const char __user *buffer, size_t count, loff_t *pos) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci char tmp[33]; 9348c2ecf20Sopenharmony_ci char *label, *val; 9358c2ecf20Sopenharmony_ci size_t fcount = count; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!count) 9388c2ecf20Sopenharmony_ci return -EINVAL; 9398c2ecf20Sopenharmony_ci if (count > 32) 9408c2ecf20Sopenharmony_ci count = 32; 9418c2ecf20Sopenharmony_ci if (copy_from_user(tmp, buffer, count)) 9428c2ecf20Sopenharmony_ci return -EFAULT; 9438c2ecf20Sopenharmony_ci tmp[count] = 0; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci label = tmp; 9468c2ecf20Sopenharmony_ci while(*label == ' ') 9478c2ecf20Sopenharmony_ci label++; 9488c2ecf20Sopenharmony_ci val = label; 9498c2ecf20Sopenharmony_ci while(*val && (*val != '=')) { 9508c2ecf20Sopenharmony_ci if (*val == ' ') 9518c2ecf20Sopenharmony_ci *val = 0; 9528c2ecf20Sopenharmony_ci val++; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci if ((*val) == 0) 9558c2ecf20Sopenharmony_ci return -EINVAL; 9568c2ecf20Sopenharmony_ci *(val++) = 0; 9578c2ecf20Sopenharmony_ci while(*val == ' ') 9588c2ecf20Sopenharmony_ci val++; 9598c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 9608c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && 9618c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) 9628c2ecf20Sopenharmony_ci if (!strcmp(label, "lid_wakeup")) 9638c2ecf20Sopenharmony_ci option_lid_wakeup = ((*val) == '1'); 9648c2ecf20Sopenharmony_ci#endif 9658c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) { 9668c2ecf20Sopenharmony_ci int new_value; 9678c2ecf20Sopenharmony_ci new_value = ((*val) == '1'); 9688c2ecf20Sopenharmony_ci if (new_value != option_server_mode) 9698c2ecf20Sopenharmony_ci pmu_set_server_mode(new_value); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci return fcount; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic const struct proc_ops pmu_options_proc_ops = { 9758c2ecf20Sopenharmony_ci .proc_open = pmu_options_proc_open, 9768c2ecf20Sopenharmony_ci .proc_read = seq_read, 9778c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 9788c2ecf20Sopenharmony_ci .proc_release = single_release, 9798c2ecf20Sopenharmony_ci .proc_write = pmu_options_proc_write, 9808c2ecf20Sopenharmony_ci}; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 9838c2ecf20Sopenharmony_ci/* Send an ADB command */ 9848c2ecf20Sopenharmony_cistatic int pmu_send_request(struct adb_request *req, int sync) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci int i, ret; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited) { 9898c2ecf20Sopenharmony_ci req->complete = 1; 9908c2ecf20Sopenharmony_ci return -ENXIO; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ret = -EINVAL; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci switch (req->data[0]) { 9968c2ecf20Sopenharmony_ci case PMU_PACKET: 9978c2ecf20Sopenharmony_ci for (i = 0; i < req->nbytes - 1; ++i) 9988c2ecf20Sopenharmony_ci req->data[i] = req->data[i+1]; 9998c2ecf20Sopenharmony_ci --req->nbytes; 10008c2ecf20Sopenharmony_ci if (pmu_data_len[req->data[0]][1] != 0) { 10018c2ecf20Sopenharmony_ci req->reply[0] = ADB_RET_OK; 10028c2ecf20Sopenharmony_ci req->reply_len = 1; 10038c2ecf20Sopenharmony_ci } else 10048c2ecf20Sopenharmony_ci req->reply_len = 0; 10058c2ecf20Sopenharmony_ci ret = pmu_queue_request(req); 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci case CUDA_PACKET: 10088c2ecf20Sopenharmony_ci switch (req->data[1]) { 10098c2ecf20Sopenharmony_ci case CUDA_GET_TIME: 10108c2ecf20Sopenharmony_ci if (req->nbytes != 2) 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci req->data[0] = PMU_READ_RTC; 10138c2ecf20Sopenharmony_ci req->nbytes = 1; 10148c2ecf20Sopenharmony_ci req->reply_len = 3; 10158c2ecf20Sopenharmony_ci req->reply[0] = CUDA_PACKET; 10168c2ecf20Sopenharmony_ci req->reply[1] = 0; 10178c2ecf20Sopenharmony_ci req->reply[2] = CUDA_GET_TIME; 10188c2ecf20Sopenharmony_ci ret = pmu_queue_request(req); 10198c2ecf20Sopenharmony_ci break; 10208c2ecf20Sopenharmony_ci case CUDA_SET_TIME: 10218c2ecf20Sopenharmony_ci if (req->nbytes != 6) 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci req->data[0] = PMU_SET_RTC; 10248c2ecf20Sopenharmony_ci req->nbytes = 5; 10258c2ecf20Sopenharmony_ci for (i = 1; i <= 4; ++i) 10268c2ecf20Sopenharmony_ci req->data[i] = req->data[i+1]; 10278c2ecf20Sopenharmony_ci req->reply_len = 3; 10288c2ecf20Sopenharmony_ci req->reply[0] = CUDA_PACKET; 10298c2ecf20Sopenharmony_ci req->reply[1] = 0; 10308c2ecf20Sopenharmony_ci req->reply[2] = CUDA_SET_TIME; 10318c2ecf20Sopenharmony_ci ret = pmu_queue_request(req); 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci break; 10358c2ecf20Sopenharmony_ci case ADB_PACKET: 10368c2ecf20Sopenharmony_ci if (!pmu_has_adb) 10378c2ecf20Sopenharmony_ci return -ENXIO; 10388c2ecf20Sopenharmony_ci for (i = req->nbytes - 1; i > 1; --i) 10398c2ecf20Sopenharmony_ci req->data[i+2] = req->data[i]; 10408c2ecf20Sopenharmony_ci req->data[3] = req->nbytes - 2; 10418c2ecf20Sopenharmony_ci req->data[2] = pmu_adb_flags; 10428c2ecf20Sopenharmony_ci /*req->data[1] = req->data[1];*/ 10438c2ecf20Sopenharmony_ci req->data[0] = PMU_ADB_CMD; 10448c2ecf20Sopenharmony_ci req->nbytes += 2; 10458c2ecf20Sopenharmony_ci req->reply_expected = 1; 10468c2ecf20Sopenharmony_ci req->reply_len = 0; 10478c2ecf20Sopenharmony_ci ret = pmu_queue_request(req); 10488c2ecf20Sopenharmony_ci break; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci if (ret) { 10518c2ecf20Sopenharmony_ci req->complete = 1; 10528c2ecf20Sopenharmony_ci return ret; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (sync) 10568c2ecf20Sopenharmony_ci while (!req->complete) 10578c2ecf20Sopenharmony_ci pmu_poll(); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 0; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci/* Enable/disable autopolling */ 10638c2ecf20Sopenharmony_cistatic int __pmu_adb_autopoll(int devs) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct adb_request req; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (devs) { 10688c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, 10698c2ecf20Sopenharmony_ci adb_dev_map >> 8, adb_dev_map); 10708c2ecf20Sopenharmony_ci pmu_adb_flags = 2; 10718c2ecf20Sopenharmony_ci } else { 10728c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF); 10738c2ecf20Sopenharmony_ci pmu_adb_flags = 0; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci while (!req.complete) 10768c2ecf20Sopenharmony_ci pmu_poll(); 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic int pmu_adb_autopoll(int devs) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited || !pmu_has_adb) 10838c2ecf20Sopenharmony_ci return -ENXIO; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci adb_dev_map = devs; 10868c2ecf20Sopenharmony_ci return __pmu_adb_autopoll(devs); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/* Reset the ADB bus */ 10908c2ecf20Sopenharmony_cistatic int pmu_adb_reset_bus(void) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci struct adb_request req; 10938c2ecf20Sopenharmony_ci int save_autopoll = adb_dev_map; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited || !pmu_has_adb) 10968c2ecf20Sopenharmony_ci return -ENXIO; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* anyone got a better idea?? */ 10998c2ecf20Sopenharmony_ci __pmu_adb_autopoll(0); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci req.nbytes = 4; 11028c2ecf20Sopenharmony_ci req.done = NULL; 11038c2ecf20Sopenharmony_ci req.data[0] = PMU_ADB_CMD; 11048c2ecf20Sopenharmony_ci req.data[1] = ADB_BUSRESET; 11058c2ecf20Sopenharmony_ci req.data[2] = 0; 11068c2ecf20Sopenharmony_ci req.data[3] = 0; 11078c2ecf20Sopenharmony_ci req.data[4] = 0; 11088c2ecf20Sopenharmony_ci req.reply_len = 0; 11098c2ecf20Sopenharmony_ci req.reply_expected = 1; 11108c2ecf20Sopenharmony_ci if (pmu_queue_request(&req) != 0) { 11118c2ecf20Sopenharmony_ci printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n"); 11128c2ecf20Sopenharmony_ci return -EIO; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (save_autopoll != 0) 11178c2ecf20Sopenharmony_ci __pmu_adb_autopoll(save_autopoll); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci return 0; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci/* Construct and send a pmu request */ 11248c2ecf20Sopenharmony_ciint 11258c2ecf20Sopenharmony_cipmu_request(struct adb_request *req, void (*done)(struct adb_request *), 11268c2ecf20Sopenharmony_ci int nbytes, ...) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci va_list list; 11298c2ecf20Sopenharmony_ci int i; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 11328c2ecf20Sopenharmony_ci return -ENXIO; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (nbytes < 0 || nbytes > 32) { 11358c2ecf20Sopenharmony_ci printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes); 11368c2ecf20Sopenharmony_ci req->complete = 1; 11378c2ecf20Sopenharmony_ci return -EINVAL; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci req->nbytes = nbytes; 11408c2ecf20Sopenharmony_ci req->done = done; 11418c2ecf20Sopenharmony_ci va_start(list, nbytes); 11428c2ecf20Sopenharmony_ci for (i = 0; i < nbytes; ++i) 11438c2ecf20Sopenharmony_ci req->data[i] = va_arg(list, int); 11448c2ecf20Sopenharmony_ci va_end(list); 11458c2ecf20Sopenharmony_ci req->reply_len = 0; 11468c2ecf20Sopenharmony_ci req->reply_expected = 0; 11478c2ecf20Sopenharmony_ci return pmu_queue_request(req); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ciint 11518c2ecf20Sopenharmony_cipmu_queue_request(struct adb_request *req) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci unsigned long flags; 11548c2ecf20Sopenharmony_ci int nsend; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) { 11578c2ecf20Sopenharmony_ci req->complete = 1; 11588c2ecf20Sopenharmony_ci return -ENXIO; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci if (req->nbytes <= 0) { 11618c2ecf20Sopenharmony_ci req->complete = 1; 11628c2ecf20Sopenharmony_ci return 0; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci nsend = pmu_data_len[req->data[0]][0]; 11658c2ecf20Sopenharmony_ci if (nsend >= 0 && req->nbytes != nsend + 1) { 11668c2ecf20Sopenharmony_ci req->complete = 1; 11678c2ecf20Sopenharmony_ci return -EINVAL; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci req->next = NULL; 11718c2ecf20Sopenharmony_ci req->sent = 0; 11728c2ecf20Sopenharmony_ci req->complete = 0; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 11758c2ecf20Sopenharmony_ci if (current_req) { 11768c2ecf20Sopenharmony_ci last_req->next = req; 11778c2ecf20Sopenharmony_ci last_req = req; 11788c2ecf20Sopenharmony_ci } else { 11798c2ecf20Sopenharmony_ci current_req = req; 11808c2ecf20Sopenharmony_ci last_req = req; 11818c2ecf20Sopenharmony_ci if (pmu_state == idle) 11828c2ecf20Sopenharmony_ci pmu_start(); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic inline void 11908c2ecf20Sopenharmony_ciwait_for_ack(void) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci /* Sightly increased the delay, I had one occurrence of the message 11938c2ecf20Sopenharmony_ci * reported 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_ci int timeout = 4000; 11968c2ecf20Sopenharmony_ci while ((in_8(&via2[B]) & TACK) == 0) { 11978c2ecf20Sopenharmony_ci if (--timeout < 0) { 11988c2ecf20Sopenharmony_ci printk(KERN_ERR "PMU not responding (!ack)\n"); 11998c2ecf20Sopenharmony_ci return; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci udelay(10); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci/* New PMU seems to be very sensitive to those timings, so we make sure 12068c2ecf20Sopenharmony_ci * PCI is flushed immediately */ 12078c2ecf20Sopenharmony_cistatic inline void 12088c2ecf20Sopenharmony_cisend_byte(int x) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci out_8(&via1[ACR], in_8(&via1[ACR]) | SR_OUT | SR_EXT); 12118c2ecf20Sopenharmony_ci out_8(&via1[SR], x); 12128c2ecf20Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) & ~TREQ); /* assert TREQ */ 12138c2ecf20Sopenharmony_ci (void)in_8(&via2[B]); 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic inline void 12178c2ecf20Sopenharmony_cirecv_byte(void) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci out_8(&via1[ACR], (in_8(&via1[ACR]) & ~SR_OUT) | SR_EXT); 12208c2ecf20Sopenharmony_ci in_8(&via1[SR]); /* resets SR */ 12218c2ecf20Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) & ~TREQ); 12228c2ecf20Sopenharmony_ci (void)in_8(&via2[B]); 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic inline void 12268c2ecf20Sopenharmony_cipmu_done(struct adb_request *req) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci void (*done)(struct adb_request *) = req->done; 12298c2ecf20Sopenharmony_ci mb(); 12308c2ecf20Sopenharmony_ci req->complete = 1; 12318c2ecf20Sopenharmony_ci /* Here, we assume that if the request has a done member, the 12328c2ecf20Sopenharmony_ci * struct request will survive to setting req->complete to 1 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci if (done) 12358c2ecf20Sopenharmony_ci (*done)(req); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic void 12398c2ecf20Sopenharmony_cipmu_start(void) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct adb_request *req; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* assert pmu_state == idle */ 12448c2ecf20Sopenharmony_ci /* get the packet to send */ 12458c2ecf20Sopenharmony_ci req = current_req; 12468c2ecf20Sopenharmony_ci if (!req || pmu_state != idle 12478c2ecf20Sopenharmony_ci || (/*req->reply_expected && */req_awaiting_reply)) 12488c2ecf20Sopenharmony_ci return; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci pmu_state = sending; 12518c2ecf20Sopenharmony_ci data_index = 1; 12528c2ecf20Sopenharmony_ci data_len = pmu_data_len[req->data[0]][0]; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* Sounds safer to make sure ACK is high before writing. This helped 12558c2ecf20Sopenharmony_ci * kill a problem with ADB and some iBooks 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci wait_for_ack(); 12588c2ecf20Sopenharmony_ci /* set the shift register to shift out and send a byte */ 12598c2ecf20Sopenharmony_ci send_byte(req->data[0]); 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_civoid 12638c2ecf20Sopenharmony_cipmu_poll(void) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 12668c2ecf20Sopenharmony_ci return; 12678c2ecf20Sopenharmony_ci if (disable_poll) 12688c2ecf20Sopenharmony_ci return; 12698c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_civoid 12738c2ecf20Sopenharmony_cipmu_poll_adb(void) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 12768c2ecf20Sopenharmony_ci return; 12778c2ecf20Sopenharmony_ci if (disable_poll) 12788c2ecf20Sopenharmony_ci return; 12798c2ecf20Sopenharmony_ci /* Kicks ADB read when PMU is suspended */ 12808c2ecf20Sopenharmony_ci adb_int_pending = 1; 12818c2ecf20Sopenharmony_ci do { 12828c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 12838c2ecf20Sopenharmony_ci } while (pmu_suspended && (adb_int_pending || pmu_state != idle 12848c2ecf20Sopenharmony_ci || req_awaiting_reply)); 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_civoid 12888c2ecf20Sopenharmony_cipmu_wait_complete(struct adb_request *req) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 12918c2ecf20Sopenharmony_ci return; 12928c2ecf20Sopenharmony_ci while((pmu_state != idle && pmu_state != locked) || !req->complete) 12938c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci/* This function loops until the PMU is idle and prevents it from 12978c2ecf20Sopenharmony_ci * anwsering to ADB interrupts. pmu_request can still be called. 12988c2ecf20Sopenharmony_ci * This is done to avoid spurrious shutdowns when we know we'll have 12998c2ecf20Sopenharmony_ci * interrupts switched off for a long time 13008c2ecf20Sopenharmony_ci */ 13018c2ecf20Sopenharmony_civoid 13028c2ecf20Sopenharmony_cipmu_suspend(void) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci unsigned long flags; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 13078c2ecf20Sopenharmony_ci return; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 13108c2ecf20Sopenharmony_ci pmu_suspended++; 13118c2ecf20Sopenharmony_ci if (pmu_suspended > 1) { 13128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci do { 13178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 13188c2ecf20Sopenharmony_ci if (req_awaiting_reply) 13198c2ecf20Sopenharmony_ci adb_int_pending = 1; 13208c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 13218c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 13228c2ecf20Sopenharmony_ci if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) { 13238c2ecf20Sopenharmony_ci if (gpio_irq >= 0) 13248c2ecf20Sopenharmony_ci disable_irq_nosync(gpio_irq); 13258c2ecf20Sopenharmony_ci out_8(&via1[IER], CB1_INT | IER_CLR); 13268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 13278c2ecf20Sopenharmony_ci break; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } while (1); 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_civoid 13338c2ecf20Sopenharmony_cipmu_resume(void) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci unsigned long flags; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (pmu_state == uninitialized || pmu_suspended < 1) 13388c2ecf20Sopenharmony_ci return; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 13418c2ecf20Sopenharmony_ci pmu_suspended--; 13428c2ecf20Sopenharmony_ci if (pmu_suspended > 0) { 13438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 13448c2ecf20Sopenharmony_ci return; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci adb_int_pending = 1; 13478c2ecf20Sopenharmony_ci if (gpio_irq >= 0) 13488c2ecf20Sopenharmony_ci enable_irq(gpio_irq); 13498c2ecf20Sopenharmony_ci out_8(&via1[IER], CB1_INT | IER_SET); 13508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 13518c2ecf20Sopenharmony_ci pmu_poll(); 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci/* Interrupt data could be the result data from an ADB cmd */ 13558c2ecf20Sopenharmony_cistatic void 13568c2ecf20Sopenharmony_cipmu_handle_data(unsigned char *data, int len) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci unsigned char ints; 13598c2ecf20Sopenharmony_ci int idx; 13608c2ecf20Sopenharmony_ci int i = 0; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci asleep = 0; 13638c2ecf20Sopenharmony_ci if (drop_interrupts || len < 1) { 13648c2ecf20Sopenharmony_ci adb_int_pending = 0; 13658c2ecf20Sopenharmony_ci pmu_irq_stats[8]++; 13668c2ecf20Sopenharmony_ci return; 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* Get PMU interrupt mask */ 13708c2ecf20Sopenharmony_ci ints = data[0]; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci /* Record zero interrupts for stats */ 13738c2ecf20Sopenharmony_ci if (ints == 0) 13748c2ecf20Sopenharmony_ci pmu_irq_stats[9]++; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* Hack to deal with ADB autopoll flag */ 13778c2ecf20Sopenharmony_ci if (ints & PMU_INT_ADB) 13788c2ecf20Sopenharmony_ci ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cinext: 13818c2ecf20Sopenharmony_ci if (ints == 0) { 13828c2ecf20Sopenharmony_ci if (i > pmu_irq_stats[10]) 13838c2ecf20Sopenharmony_ci pmu_irq_stats[10] = i; 13848c2ecf20Sopenharmony_ci return; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci i++; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci idx = ffs(ints) - 1; 13898c2ecf20Sopenharmony_ci ints &= ~BIT(idx); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci pmu_irq_stats[idx]++; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* Note: for some reason, we get an interrupt with len=1, 13948c2ecf20Sopenharmony_ci * data[0]==0 after each normal ADB interrupt, at least 13958c2ecf20Sopenharmony_ci * on the Pismo. Still investigating... --BenH 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci switch (BIT(idx)) { 13988c2ecf20Sopenharmony_ci case PMU_INT_ADB: 13998c2ecf20Sopenharmony_ci if ((data[0] & PMU_INT_ADB_AUTO) == 0) { 14008c2ecf20Sopenharmony_ci struct adb_request *req = req_awaiting_reply; 14018c2ecf20Sopenharmony_ci if (!req) { 14028c2ecf20Sopenharmony_ci printk(KERN_ERR "PMU: extra ADB reply\n"); 14038c2ecf20Sopenharmony_ci return; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci req_awaiting_reply = NULL; 14068c2ecf20Sopenharmony_ci if (len <= 2) 14078c2ecf20Sopenharmony_ci req->reply_len = 0; 14088c2ecf20Sopenharmony_ci else { 14098c2ecf20Sopenharmony_ci memcpy(req->reply, data + 1, len - 1); 14108c2ecf20Sopenharmony_ci req->reply_len = len - 1; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci pmu_done(req); 14138c2ecf20Sopenharmony_ci } else { 14148c2ecf20Sopenharmony_ci#ifdef CONFIG_XMON 14158c2ecf20Sopenharmony_ci if (len == 4 && data[1] == 0x2c) { 14168c2ecf20Sopenharmony_ci extern int xmon_wants_key, xmon_adb_keycode; 14178c2ecf20Sopenharmony_ci if (xmon_wants_key) { 14188c2ecf20Sopenharmony_ci xmon_adb_keycode = data[2]; 14198c2ecf20Sopenharmony_ci return; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci#endif /* CONFIG_XMON */ 14238c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 14248c2ecf20Sopenharmony_ci /* 14258c2ecf20Sopenharmony_ci * XXX On the [23]400 the PMU gives us an up 14268c2ecf20Sopenharmony_ci * event for keycodes 0x74 or 0x75 when the PC 14278c2ecf20Sopenharmony_ci * card eject buttons are released, so we 14288c2ecf20Sopenharmony_ci * ignore those events. 14298c2ecf20Sopenharmony_ci */ 14308c2ecf20Sopenharmony_ci if (!(pmu_kind == PMU_OHARE_BASED && len == 4 14318c2ecf20Sopenharmony_ci && data[1] == 0x2c && data[3] == 0xff 14328c2ecf20Sopenharmony_ci && (data[2] & ~1) == 0xf4)) 14338c2ecf20Sopenharmony_ci adb_input(data+1, len-1, 1); 14348c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci break; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* Sound/brightness button pressed */ 14398c2ecf20Sopenharmony_ci case PMU_INT_SNDBRT: 14408c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 14418c2ecf20Sopenharmony_ci if (len == 3) 14428c2ecf20Sopenharmony_ci pmac_backlight_set_legacy_brightness_pmu(data[1] >> 4); 14438c2ecf20Sopenharmony_ci#endif 14448c2ecf20Sopenharmony_ci break; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* Tick interrupt */ 14478c2ecf20Sopenharmony_ci case PMU_INT_TICK: 14488c2ecf20Sopenharmony_ci /* Environment or tick interrupt, query batteries */ 14498c2ecf20Sopenharmony_ci if (pmu_battery_count) { 14508c2ecf20Sopenharmony_ci if ((--query_batt_timer) == 0) { 14518c2ecf20Sopenharmony_ci query_battery_state(); 14528c2ecf20Sopenharmony_ci query_batt_timer = BATTERY_POLLING_COUNT; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci case PMU_INT_ENVIRONMENT: 14588c2ecf20Sopenharmony_ci if (pmu_battery_count) 14598c2ecf20Sopenharmony_ci query_battery_state(); 14608c2ecf20Sopenharmony_ci pmu_pass_intr(data, len); 14618c2ecf20Sopenharmony_ci /* len == 6 is probably a bad check. But how do I 14628c2ecf20Sopenharmony_ci * know what PMU versions send what events here? */ 14638c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ADB_PMU_EVENT) && len == 6) { 14648c2ecf20Sopenharmony_ci via_pmu_event(PMU_EVT_POWER, !!(data[1]&8)); 14658c2ecf20Sopenharmony_ci via_pmu_event(PMU_EVT_LID, data[1]&1); 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci default: 14708c2ecf20Sopenharmony_ci pmu_pass_intr(data, len); 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci goto next; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic struct adb_request* 14768c2ecf20Sopenharmony_cipmu_sr_intr(void) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci struct adb_request *req; 14798c2ecf20Sopenharmony_ci int bite = 0; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if (in_8(&via2[B]) & TREQ) { 14828c2ecf20Sopenharmony_ci printk(KERN_ERR "PMU: spurious SR intr (%x)\n", in_8(&via2[B])); 14838c2ecf20Sopenharmony_ci return NULL; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci /* The ack may not yet be low when we get the interrupt */ 14868c2ecf20Sopenharmony_ci while ((in_8(&via2[B]) & TACK) != 0) 14878c2ecf20Sopenharmony_ci ; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci /* if reading grab the byte, and reset the interrupt */ 14908c2ecf20Sopenharmony_ci if (pmu_state == reading || pmu_state == reading_intr) 14918c2ecf20Sopenharmony_ci bite = in_8(&via1[SR]); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci /* reset TREQ and wait for TACK to go high */ 14948c2ecf20Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) | TREQ); 14958c2ecf20Sopenharmony_ci wait_for_ack(); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci switch (pmu_state) { 14988c2ecf20Sopenharmony_ci case sending: 14998c2ecf20Sopenharmony_ci req = current_req; 15008c2ecf20Sopenharmony_ci if (data_len < 0) { 15018c2ecf20Sopenharmony_ci data_len = req->nbytes - 1; 15028c2ecf20Sopenharmony_ci send_byte(data_len); 15038c2ecf20Sopenharmony_ci break; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci if (data_index <= data_len) { 15068c2ecf20Sopenharmony_ci send_byte(req->data[data_index++]); 15078c2ecf20Sopenharmony_ci break; 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci req->sent = 1; 15108c2ecf20Sopenharmony_ci data_len = pmu_data_len[req->data[0]][1]; 15118c2ecf20Sopenharmony_ci if (data_len == 0) { 15128c2ecf20Sopenharmony_ci pmu_state = idle; 15138c2ecf20Sopenharmony_ci current_req = req->next; 15148c2ecf20Sopenharmony_ci if (req->reply_expected) 15158c2ecf20Sopenharmony_ci req_awaiting_reply = req; 15168c2ecf20Sopenharmony_ci else 15178c2ecf20Sopenharmony_ci return req; 15188c2ecf20Sopenharmony_ci } else { 15198c2ecf20Sopenharmony_ci pmu_state = reading; 15208c2ecf20Sopenharmony_ci data_index = 0; 15218c2ecf20Sopenharmony_ci reply_ptr = req->reply + req->reply_len; 15228c2ecf20Sopenharmony_ci recv_byte(); 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci break; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci case intack: 15278c2ecf20Sopenharmony_ci data_index = 0; 15288c2ecf20Sopenharmony_ci data_len = -1; 15298c2ecf20Sopenharmony_ci pmu_state = reading_intr; 15308c2ecf20Sopenharmony_ci reply_ptr = interrupt_data[int_data_last]; 15318c2ecf20Sopenharmony_ci recv_byte(); 15328c2ecf20Sopenharmony_ci if (gpio_irq >= 0 && !gpio_irq_enabled) { 15338c2ecf20Sopenharmony_ci enable_irq(gpio_irq); 15348c2ecf20Sopenharmony_ci gpio_irq_enabled = 1; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci break; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci case reading: 15398c2ecf20Sopenharmony_ci case reading_intr: 15408c2ecf20Sopenharmony_ci if (data_len == -1) { 15418c2ecf20Sopenharmony_ci data_len = bite; 15428c2ecf20Sopenharmony_ci if (bite > 32) 15438c2ecf20Sopenharmony_ci printk(KERN_ERR "PMU: bad reply len %d\n", bite); 15448c2ecf20Sopenharmony_ci } else if (data_index < 32) { 15458c2ecf20Sopenharmony_ci reply_ptr[data_index++] = bite; 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci if (data_index < data_len) { 15488c2ecf20Sopenharmony_ci recv_byte(); 15498c2ecf20Sopenharmony_ci break; 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (pmu_state == reading_intr) { 15538c2ecf20Sopenharmony_ci pmu_state = idle; 15548c2ecf20Sopenharmony_ci int_data_state[int_data_last] = int_data_ready; 15558c2ecf20Sopenharmony_ci interrupt_data_len[int_data_last] = data_len; 15568c2ecf20Sopenharmony_ci } else { 15578c2ecf20Sopenharmony_ci req = current_req; 15588c2ecf20Sopenharmony_ci /* 15598c2ecf20Sopenharmony_ci * For PMU sleep and freq change requests, we lock the 15608c2ecf20Sopenharmony_ci * PMU until it's explicitly unlocked. This avoids any 15618c2ecf20Sopenharmony_ci * spurrious event polling getting in 15628c2ecf20Sopenharmony_ci */ 15638c2ecf20Sopenharmony_ci current_req = req->next; 15648c2ecf20Sopenharmony_ci req->reply_len += data_index; 15658c2ecf20Sopenharmony_ci if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED) 15668c2ecf20Sopenharmony_ci pmu_state = locked; 15678c2ecf20Sopenharmony_ci else 15688c2ecf20Sopenharmony_ci pmu_state = idle; 15698c2ecf20Sopenharmony_ci return req; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci default: 15748c2ecf20Sopenharmony_ci printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n", 15758c2ecf20Sopenharmony_ci pmu_state); 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci return NULL; 15788c2ecf20Sopenharmony_ci} 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_cistatic irqreturn_t 15818c2ecf20Sopenharmony_civia_pmu_interrupt(int irq, void *arg) 15828c2ecf20Sopenharmony_ci{ 15838c2ecf20Sopenharmony_ci unsigned long flags; 15848c2ecf20Sopenharmony_ci int intr; 15858c2ecf20Sopenharmony_ci int nloop = 0; 15868c2ecf20Sopenharmony_ci int int_data = -1; 15878c2ecf20Sopenharmony_ci struct adb_request *req = NULL; 15888c2ecf20Sopenharmony_ci int handled = 0; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* This is a bit brutal, we can probably do better */ 15918c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 15928c2ecf20Sopenharmony_ci ++disable_poll; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci for (;;) { 15958c2ecf20Sopenharmony_ci /* On 68k Macs, VIA interrupts are dispatched individually. 15968c2ecf20Sopenharmony_ci * Unless we are polling, the relevant IRQ flag has already 15978c2ecf20Sopenharmony_ci * been cleared. 15988c2ecf20Sopenharmony_ci */ 15998c2ecf20Sopenharmony_ci intr = 0; 16008c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_PMAC) || !irq) { 16018c2ecf20Sopenharmony_ci intr = in_8(&via1[IFR]) & (SR_INT | CB1_INT); 16028c2ecf20Sopenharmony_ci out_8(&via1[IFR], intr); 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 16058c2ecf20Sopenharmony_ci switch (irq) { 16068c2ecf20Sopenharmony_ci case IRQ_MAC_ADB_CL: 16078c2ecf20Sopenharmony_ci intr = CB1_INT; 16088c2ecf20Sopenharmony_ci break; 16098c2ecf20Sopenharmony_ci case IRQ_MAC_ADB_SR: 16108c2ecf20Sopenharmony_ci intr = SR_INT; 16118c2ecf20Sopenharmony_ci break; 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci#endif 16148c2ecf20Sopenharmony_ci if (intr == 0) 16158c2ecf20Sopenharmony_ci break; 16168c2ecf20Sopenharmony_ci handled = 1; 16178c2ecf20Sopenharmony_ci if (++nloop > 1000) { 16188c2ecf20Sopenharmony_ci printk(KERN_DEBUG "PMU: stuck in intr loop, " 16198c2ecf20Sopenharmony_ci "intr=%x, ier=%x pmu_state=%d\n", 16208c2ecf20Sopenharmony_ci intr, in_8(&via1[IER]), pmu_state); 16218c2ecf20Sopenharmony_ci break; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci if (intr & CB1_INT) { 16248c2ecf20Sopenharmony_ci adb_int_pending = 1; 16258c2ecf20Sopenharmony_ci pmu_irq_stats[11]++; 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ci if (intr & SR_INT) { 16288c2ecf20Sopenharmony_ci req = pmu_sr_intr(); 16298c2ecf20Sopenharmony_ci if (req) 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 16338c2ecf20Sopenharmony_ci break; 16348c2ecf20Sopenharmony_ci#endif 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cirecheck: 16388c2ecf20Sopenharmony_ci if (pmu_state == idle) { 16398c2ecf20Sopenharmony_ci if (adb_int_pending) { 16408c2ecf20Sopenharmony_ci if (int_data_state[0] == int_data_empty) 16418c2ecf20Sopenharmony_ci int_data_last = 0; 16428c2ecf20Sopenharmony_ci else if (int_data_state[1] == int_data_empty) 16438c2ecf20Sopenharmony_ci int_data_last = 1; 16448c2ecf20Sopenharmony_ci else 16458c2ecf20Sopenharmony_ci goto no_free_slot; 16468c2ecf20Sopenharmony_ci pmu_state = intack; 16478c2ecf20Sopenharmony_ci int_data_state[int_data_last] = int_data_fill; 16488c2ecf20Sopenharmony_ci /* Sounds safer to make sure ACK is high before writing. 16498c2ecf20Sopenharmony_ci * This helped kill a problem with ADB and some iBooks 16508c2ecf20Sopenharmony_ci */ 16518c2ecf20Sopenharmony_ci wait_for_ack(); 16528c2ecf20Sopenharmony_ci send_byte(PMU_INT_ACK); 16538c2ecf20Sopenharmony_ci adb_int_pending = 0; 16548c2ecf20Sopenharmony_ci } else if (current_req) 16558c2ecf20Sopenharmony_ci pmu_start(); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_cino_free_slot: 16588c2ecf20Sopenharmony_ci /* Mark the oldest buffer for flushing */ 16598c2ecf20Sopenharmony_ci if (int_data_state[!int_data_last] == int_data_ready) { 16608c2ecf20Sopenharmony_ci int_data_state[!int_data_last] = int_data_flush; 16618c2ecf20Sopenharmony_ci int_data = !int_data_last; 16628c2ecf20Sopenharmony_ci } else if (int_data_state[int_data_last] == int_data_ready) { 16638c2ecf20Sopenharmony_ci int_data_state[int_data_last] = int_data_flush; 16648c2ecf20Sopenharmony_ci int_data = int_data_last; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci --disable_poll; 16678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci /* Deal with completed PMU requests outside of the lock */ 16708c2ecf20Sopenharmony_ci if (req) { 16718c2ecf20Sopenharmony_ci pmu_done(req); 16728c2ecf20Sopenharmony_ci req = NULL; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* Deal with interrupt datas outside of the lock */ 16768c2ecf20Sopenharmony_ci if (int_data >= 0) { 16778c2ecf20Sopenharmony_ci pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data]); 16788c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 16798c2ecf20Sopenharmony_ci ++disable_poll; 16808c2ecf20Sopenharmony_ci int_data_state[int_data] = int_data_empty; 16818c2ecf20Sopenharmony_ci int_data = -1; 16828c2ecf20Sopenharmony_ci goto recheck; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_civoid 16898c2ecf20Sopenharmony_cipmu_unlock(void) 16908c2ecf20Sopenharmony_ci{ 16918c2ecf20Sopenharmony_ci unsigned long flags; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 16948c2ecf20Sopenharmony_ci if (pmu_state == locked) 16958c2ecf20Sopenharmony_ci pmu_state = idle; 16968c2ecf20Sopenharmony_ci adb_int_pending = 1; 16978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic __maybe_unused irqreturn_t 17028c2ecf20Sopenharmony_cigpio1_interrupt(int irq, void *arg) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci unsigned long flags; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { 17078c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 17088c2ecf20Sopenharmony_ci if (gpio_irq_enabled > 0) { 17098c2ecf20Sopenharmony_ci disable_irq_nosync(gpio_irq); 17108c2ecf20Sopenharmony_ci gpio_irq_enabled = 0; 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci pmu_irq_stats[12]++; 17138c2ecf20Sopenharmony_ci adb_int_pending = 1; 17148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 17158c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 17168c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci return IRQ_NONE; 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_civoid 17228c2ecf20Sopenharmony_cipmu_enable_irled(int on) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct adb_request req; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 17278c2ecf20Sopenharmony_ci return ; 17288c2ecf20Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) 17298c2ecf20Sopenharmony_ci return ; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED | 17328c2ecf20Sopenharmony_ci (on ? PMU_POW_ON : PMU_POW_OFF)); 17338c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci/* Offset between Unix time (1970-based) and Mac time (1904-based) */ 17378c2ecf20Sopenharmony_ci#define RTC_OFFSET 2082844800 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_citime64_t pmu_get_time(void) 17408c2ecf20Sopenharmony_ci{ 17418c2ecf20Sopenharmony_ci struct adb_request req; 17428c2ecf20Sopenharmony_ci u32 now; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) 17458c2ecf20Sopenharmony_ci return 0; 17468c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 17478c2ecf20Sopenharmony_ci if (req.reply_len != 4) 17488c2ecf20Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 17498c2ecf20Sopenharmony_ci now = (req.reply[0] << 24) + (req.reply[1] << 16) + 17508c2ecf20Sopenharmony_ci (req.reply[2] << 8) + req.reply[3]; 17518c2ecf20Sopenharmony_ci return (time64_t)now - RTC_OFFSET; 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ciint pmu_set_rtc_time(struct rtc_time *tm) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci u32 now; 17578c2ecf20Sopenharmony_ci struct adb_request req; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci now = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET); 17608c2ecf20Sopenharmony_ci if (pmu_request(&req, NULL, 5, PMU_SET_RTC, 17618c2ecf20Sopenharmony_ci now >> 24, now >> 16, now >> 8, now) < 0) 17628c2ecf20Sopenharmony_ci return -ENXIO; 17638c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 17648c2ecf20Sopenharmony_ci if (req.reply_len != 0) 17658c2ecf20Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 17668c2ecf20Sopenharmony_ci return 0; 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_civoid 17708c2ecf20Sopenharmony_cipmu_restart(void) 17718c2ecf20Sopenharmony_ci{ 17728c2ecf20Sopenharmony_ci struct adb_request req; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 17758c2ecf20Sopenharmony_ci return; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci local_irq_disable(); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci drop_interrupts = 1; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) { 17828c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | 17838c2ecf20Sopenharmony_ci PMU_INT_TICK ); 17848c2ecf20Sopenharmony_ci while(!req.complete) 17858c2ecf20Sopenharmony_ci pmu_poll(); 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_RESET); 17898c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 17908c2ecf20Sopenharmony_ci for (;;) 17918c2ecf20Sopenharmony_ci ; 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_civoid 17958c2ecf20Sopenharmony_cipmu_shutdown(void) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci struct adb_request req; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 18008c2ecf20Sopenharmony_ci return; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci local_irq_disable(); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci drop_interrupts = 1; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) { 18078c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | 18088c2ecf20Sopenharmony_ci PMU_INT_TICK ); 18098c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 18108c2ecf20Sopenharmony_ci } else { 18118c2ecf20Sopenharmony_ci /* Disable server mode on shutdown or we'll just 18128c2ecf20Sopenharmony_ci * wake up again 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_ci pmu_set_server_mode(0); 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SHUTDOWN, 18188c2ecf20Sopenharmony_ci 'M', 'A', 'T', 'T'); 18198c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 18208c2ecf20Sopenharmony_ci for (;;) 18218c2ecf20Sopenharmony_ci ; 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ciint 18258c2ecf20Sopenharmony_cipmu_present(void) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci return pmu_state != uninitialized; 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 18318c2ecf20Sopenharmony_ci/* 18328c2ecf20Sopenharmony_ci * Put the powerbook to sleep. 18338c2ecf20Sopenharmony_ci */ 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_cistatic u32 save_via[8]; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic void 18388c2ecf20Sopenharmony_cisave_via_state(void) 18398c2ecf20Sopenharmony_ci{ 18408c2ecf20Sopenharmony_ci save_via[0] = in_8(&via1[ANH]); 18418c2ecf20Sopenharmony_ci save_via[1] = in_8(&via1[DIRA]); 18428c2ecf20Sopenharmony_ci save_via[2] = in_8(&via1[B]); 18438c2ecf20Sopenharmony_ci save_via[3] = in_8(&via1[DIRB]); 18448c2ecf20Sopenharmony_ci save_via[4] = in_8(&via1[PCR]); 18458c2ecf20Sopenharmony_ci save_via[5] = in_8(&via1[ACR]); 18468c2ecf20Sopenharmony_ci save_via[6] = in_8(&via1[T1CL]); 18478c2ecf20Sopenharmony_ci save_via[7] = in_8(&via1[T1CH]); 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_cistatic void 18508c2ecf20Sopenharmony_cirestore_via_state(void) 18518c2ecf20Sopenharmony_ci{ 18528c2ecf20Sopenharmony_ci out_8(&via1[ANH], save_via[0]); 18538c2ecf20Sopenharmony_ci out_8(&via1[DIRA], save_via[1]); 18548c2ecf20Sopenharmony_ci out_8(&via1[B], save_via[2]); 18558c2ecf20Sopenharmony_ci out_8(&via1[DIRB], save_via[3]); 18568c2ecf20Sopenharmony_ci out_8(&via1[PCR], save_via[4]); 18578c2ecf20Sopenharmony_ci out_8(&via1[ACR], save_via[5]); 18588c2ecf20Sopenharmony_ci out_8(&via1[T1CL], save_via[6]); 18598c2ecf20Sopenharmony_ci out_8(&via1[T1CH], save_via[7]); 18608c2ecf20Sopenharmony_ci out_8(&via1[IER], IER_CLR | 0x7f); /* disable all intrs */ 18618c2ecf20Sopenharmony_ci out_8(&via1[IFR], 0x7f); /* clear IFR */ 18628c2ecf20Sopenharmony_ci out_8(&via1[IER], IER_SET | SR_INT | CB1_INT); 18638c2ecf20Sopenharmony_ci} 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci#define GRACKLE_PM (1<<7) 18668c2ecf20Sopenharmony_ci#define GRACKLE_DOZE (1<<5) 18678c2ecf20Sopenharmony_ci#define GRACKLE_NAP (1<<4) 18688c2ecf20Sopenharmony_ci#define GRACKLE_SLEEP (1<<3) 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic int powerbook_sleep_grackle(void) 18718c2ecf20Sopenharmony_ci{ 18728c2ecf20Sopenharmony_ci unsigned long save_l2cr; 18738c2ecf20Sopenharmony_ci unsigned short pmcr1; 18748c2ecf20Sopenharmony_ci struct adb_request req; 18758c2ecf20Sopenharmony_ci struct pci_dev *grackle; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci grackle = pci_get_domain_bus_and_slot(0, 0, 0); 18788c2ecf20Sopenharmony_ci if (!grackle) 18798c2ecf20Sopenharmony_ci return -ENODEV; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* Turn off various things. Darwin does some retry tests here... */ 18828c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE); 18838c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 18848c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 18858c2ecf20Sopenharmony_ci PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY); 18868c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* For 750, save backside cache setting and disable it */ 18898c2ecf20Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (!__fake_sleep) { 18928c2ecf20Sopenharmony_ci /* Ask the PMU to put us to sleep */ 18938c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 18948c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci /* The VIA is supposed not to be restored correctly*/ 18988c2ecf20Sopenharmony_ci save_via_state(); 18998c2ecf20Sopenharmony_ci /* We shut down some HW */ 19008c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci pci_read_config_word(grackle, 0x70, &pmcr1); 19038c2ecf20Sopenharmony_ci /* Apparently, MacOS uses NAP mode for Grackle ??? */ 19048c2ecf20Sopenharmony_ci pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); 19058c2ecf20Sopenharmony_ci pmcr1 |= GRACKLE_PM|GRACKLE_NAP; 19068c2ecf20Sopenharmony_ci pci_write_config_word(grackle, 0x70, pmcr1); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci /* Call low-level ASM sleep handler */ 19098c2ecf20Sopenharmony_ci if (__fake_sleep) 19108c2ecf20Sopenharmony_ci mdelay(5000); 19118c2ecf20Sopenharmony_ci else 19128c2ecf20Sopenharmony_ci low_sleep_handler(); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* We're awake again, stop grackle PM */ 19158c2ecf20Sopenharmony_ci pci_read_config_word(grackle, 0x70, &pmcr1); 19168c2ecf20Sopenharmony_ci pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); 19178c2ecf20Sopenharmony_ci pci_write_config_word(grackle, 0x70, pmcr1); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci pci_dev_put(grackle); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci /* Make sure the PMU is idle */ 19228c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); 19238c2ecf20Sopenharmony_ci restore_via_state(); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* Restore L2 cache */ 19268c2ecf20Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 19278c2ecf20Sopenharmony_ci _set_L2CR(save_l2cr); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci /* Restore userland MMU context */ 19308c2ecf20Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* Power things up */ 19338c2ecf20Sopenharmony_ci pmu_unlock(); 19348c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 19358c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19368c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, 19378c2ecf20Sopenharmony_ci PMU_POW0_ON|PMU_POW0_HARD_DRIVE); 19388c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19398c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 19408c2ecf20Sopenharmony_ci PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY); 19418c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci return 0; 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic int 19478c2ecf20Sopenharmony_cipowerbook_sleep_Core99(void) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci unsigned long save_l2cr; 19508c2ecf20Sopenharmony_ci unsigned long save_l3cr; 19518c2ecf20Sopenharmony_ci struct adb_request req; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) { 19548c2ecf20Sopenharmony_ci printk(KERN_ERR "Sleep mode not supported on this machine\n"); 19558c2ecf20Sopenharmony_ci return -ENOSYS; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (num_online_cpus() > 1 || cpu_is_offline(0)) 19598c2ecf20Sopenharmony_ci return -EAGAIN; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci /* Stop environment and ADB interrupts */ 19628c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0); 19638c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci /* Tell PMU what events will wake us up */ 19668c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS, 19678c2ecf20Sopenharmony_ci 0xff, 0xff); 19688c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19698c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS, 19708c2ecf20Sopenharmony_ci 0, PMU_PWR_WAKEUP_KEY | 19718c2ecf20Sopenharmony_ci (option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0)); 19728c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci /* Save the state of the L2 and L3 caches */ 19758c2ecf20Sopenharmony_ci save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ 19768c2ecf20Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci if (!__fake_sleep) { 19798c2ecf20Sopenharmony_ci /* Ask the PMU to put us to sleep */ 19808c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 19818c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci /* The VIA is supposed not to be restored correctly*/ 19858c2ecf20Sopenharmony_ci save_via_state(); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* Shut down various ASICs. There's a chance that we can no longer 19888c2ecf20Sopenharmony_ci * talk to the PMU after this, so I moved it to _after_ sending the 19898c2ecf20Sopenharmony_ci * sleep command to it. Still need to be checked. 19908c2ecf20Sopenharmony_ci */ 19918c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci /* Call low-level ASM sleep handler */ 19948c2ecf20Sopenharmony_ci if (__fake_sleep) 19958c2ecf20Sopenharmony_ci mdelay(5000); 19968c2ecf20Sopenharmony_ci else 19978c2ecf20Sopenharmony_ci low_sleep_handler(); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci /* Restore Apple core ASICs state */ 20008c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci /* Restore VIA */ 20038c2ecf20Sopenharmony_ci restore_via_state(); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci /* tweak LPJ before cpufreq is there */ 20068c2ecf20Sopenharmony_ci loops_per_jiffy *= 2; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* Restore video */ 20098c2ecf20Sopenharmony_ci pmac_call_early_video_resume(); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci /* Restore L2 cache */ 20128c2ecf20Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 20138c2ecf20Sopenharmony_ci _set_L2CR(save_l2cr); 20148c2ecf20Sopenharmony_ci /* Restore L3 cache */ 20158c2ecf20Sopenharmony_ci if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) 20168c2ecf20Sopenharmony_ci _set_L3CR(save_l3cr); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* Restore userland MMU context */ 20198c2ecf20Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* Tell PMU we are ready */ 20228c2ecf20Sopenharmony_ci pmu_unlock(); 20238c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 20248c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 20258c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 20268c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci /* Restore LPJ, cpufreq will adjust the cpu frequency */ 20298c2ecf20Sopenharmony_ci loops_per_jiffy /= 2; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci return 0; 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci#define PB3400_MEM_CTRL 0xf8000000 20358c2ecf20Sopenharmony_ci#define PB3400_MEM_CTRL_SLEEP 0x70 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cistatic void __iomem *pb3400_mem_ctrl; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_cistatic void powerbook_sleep_init_3400(void) 20408c2ecf20Sopenharmony_ci{ 20418c2ecf20Sopenharmony_ci /* map in the memory controller registers */ 20428c2ecf20Sopenharmony_ci pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100); 20438c2ecf20Sopenharmony_ci if (pb3400_mem_ctrl == NULL) 20448c2ecf20Sopenharmony_ci printk(KERN_WARNING "ioremap failed: sleep won't be possible"); 20458c2ecf20Sopenharmony_ci} 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_cistatic int powerbook_sleep_3400(void) 20488c2ecf20Sopenharmony_ci{ 20498c2ecf20Sopenharmony_ci int i, x; 20508c2ecf20Sopenharmony_ci unsigned int hid0; 20518c2ecf20Sopenharmony_ci unsigned long msr; 20528c2ecf20Sopenharmony_ci struct adb_request sleep_req; 20538c2ecf20Sopenharmony_ci unsigned int __iomem *mem_ctrl_sleep; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (pb3400_mem_ctrl == NULL) 20568c2ecf20Sopenharmony_ci return -ENOMEM; 20578c2ecf20Sopenharmony_ci mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci /* Set the memory controller to keep the memory refreshed 20608c2ecf20Sopenharmony_ci while we're asleep */ 20618c2ecf20Sopenharmony_ci for (i = 0x403f; i >= 0x4000; --i) { 20628c2ecf20Sopenharmony_ci out_be32(mem_ctrl_sleep, i); 20638c2ecf20Sopenharmony_ci do { 20648c2ecf20Sopenharmony_ci x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff; 20658c2ecf20Sopenharmony_ci } while (x == 0); 20668c2ecf20Sopenharmony_ci if (x >= 0x100) 20678c2ecf20Sopenharmony_ci break; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci /* Ask the PMU to put us to sleep */ 20718c2ecf20Sopenharmony_ci pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 20728c2ecf20Sopenharmony_ci pmu_wait_complete(&sleep_req); 20738c2ecf20Sopenharmony_ci pmu_unlock(); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci asleep = 1; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci /* Put the CPU into sleep mode */ 20808c2ecf20Sopenharmony_ci hid0 = mfspr(SPRN_HID0); 20818c2ecf20Sopenharmony_ci hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP; 20828c2ecf20Sopenharmony_ci mtspr(SPRN_HID0, hid0); 20838c2ecf20Sopenharmony_ci local_irq_enable(); 20848c2ecf20Sopenharmony_ci msr = mfmsr() | MSR_POW; 20858c2ecf20Sopenharmony_ci while (asleep) { 20868c2ecf20Sopenharmony_ci mb(); 20878c2ecf20Sopenharmony_ci mtmsr(msr); 20888c2ecf20Sopenharmony_ci isync(); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci local_irq_disable(); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci /* OK, we're awake again, start restoring things */ 20938c2ecf20Sopenharmony_ci out_be32(mem_ctrl_sleep, 0x3f); 20948c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci return 0; 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci/* 21028c2ecf20Sopenharmony_ci * Support for /dev/pmu device 21038c2ecf20Sopenharmony_ci */ 21048c2ecf20Sopenharmony_ci#define RB_SIZE 0x10 21058c2ecf20Sopenharmony_cistruct pmu_private { 21068c2ecf20Sopenharmony_ci struct list_head list; 21078c2ecf20Sopenharmony_ci int rb_get; 21088c2ecf20Sopenharmony_ci int rb_put; 21098c2ecf20Sopenharmony_ci struct rb_entry { 21108c2ecf20Sopenharmony_ci unsigned short len; 21118c2ecf20Sopenharmony_ci unsigned char data[16]; 21128c2ecf20Sopenharmony_ci } rb_buf[RB_SIZE]; 21138c2ecf20Sopenharmony_ci wait_queue_head_t wait; 21148c2ecf20Sopenharmony_ci spinlock_t lock; 21158c2ecf20Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 21168c2ecf20Sopenharmony_ci int backlight_locker; 21178c2ecf20Sopenharmony_ci#endif 21188c2ecf20Sopenharmony_ci}; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_cistatic LIST_HEAD(all_pmu_pvt); 21218c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(all_pvt_lock); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_cistatic void 21248c2ecf20Sopenharmony_cipmu_pass_intr(unsigned char *data, int len) 21258c2ecf20Sopenharmony_ci{ 21268c2ecf20Sopenharmony_ci struct pmu_private *pp; 21278c2ecf20Sopenharmony_ci struct list_head *list; 21288c2ecf20Sopenharmony_ci int i; 21298c2ecf20Sopenharmony_ci unsigned long flags; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (len > sizeof(pp->rb_buf[0].data)) 21328c2ecf20Sopenharmony_ci len = sizeof(pp->rb_buf[0].data); 21338c2ecf20Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 21348c2ecf20Sopenharmony_ci for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) { 21358c2ecf20Sopenharmony_ci pp = list_entry(list, struct pmu_private, list); 21368c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 21378c2ecf20Sopenharmony_ci i = pp->rb_put + 1; 21388c2ecf20Sopenharmony_ci if (i >= RB_SIZE) 21398c2ecf20Sopenharmony_ci i = 0; 21408c2ecf20Sopenharmony_ci if (i != pp->rb_get) { 21418c2ecf20Sopenharmony_ci struct rb_entry *rp = &pp->rb_buf[pp->rb_put]; 21428c2ecf20Sopenharmony_ci rp->len = len; 21438c2ecf20Sopenharmony_ci memcpy(rp->data, data, len); 21448c2ecf20Sopenharmony_ci pp->rb_put = i; 21458c2ecf20Sopenharmony_ci wake_up_interruptible(&pp->wait); 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 21488c2ecf20Sopenharmony_ci } 21498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 21508c2ecf20Sopenharmony_ci} 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic int 21538c2ecf20Sopenharmony_cipmu_open(struct inode *inode, struct file *file) 21548c2ecf20Sopenharmony_ci{ 21558c2ecf20Sopenharmony_ci struct pmu_private *pp; 21568c2ecf20Sopenharmony_ci unsigned long flags; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL); 21598c2ecf20Sopenharmony_ci if (!pp) 21608c2ecf20Sopenharmony_ci return -ENOMEM; 21618c2ecf20Sopenharmony_ci pp->rb_get = pp->rb_put = 0; 21628c2ecf20Sopenharmony_ci spin_lock_init(&pp->lock); 21638c2ecf20Sopenharmony_ci init_waitqueue_head(&pp->wait); 21648c2ecf20Sopenharmony_ci mutex_lock(&pmu_info_proc_mutex); 21658c2ecf20Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 21668c2ecf20Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 21678c2ecf20Sopenharmony_ci pp->backlight_locker = 0; 21688c2ecf20Sopenharmony_ci#endif 21698c2ecf20Sopenharmony_ci list_add(&pp->list, &all_pmu_pvt); 21708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 21718c2ecf20Sopenharmony_ci file->private_data = pp; 21728c2ecf20Sopenharmony_ci mutex_unlock(&pmu_info_proc_mutex); 21738c2ecf20Sopenharmony_ci return 0; 21748c2ecf20Sopenharmony_ci} 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_cistatic ssize_t 21778c2ecf20Sopenharmony_cipmu_read(struct file *file, char __user *buf, 21788c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 21798c2ecf20Sopenharmony_ci{ 21808c2ecf20Sopenharmony_ci struct pmu_private *pp = file->private_data; 21818c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 21828c2ecf20Sopenharmony_ci unsigned long flags; 21838c2ecf20Sopenharmony_ci int ret = 0; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci if (count < 1 || !pp) 21868c2ecf20Sopenharmony_ci return -EINVAL; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 21898c2ecf20Sopenharmony_ci add_wait_queue(&pp->wait, &wait); 21908c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci for (;;) { 21938c2ecf20Sopenharmony_ci ret = -EAGAIN; 21948c2ecf20Sopenharmony_ci if (pp->rb_get != pp->rb_put) { 21958c2ecf20Sopenharmony_ci int i = pp->rb_get; 21968c2ecf20Sopenharmony_ci struct rb_entry *rp = &pp->rb_buf[i]; 21978c2ecf20Sopenharmony_ci ret = rp->len; 21988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 21998c2ecf20Sopenharmony_ci if (ret > count) 22008c2ecf20Sopenharmony_ci ret = count; 22018c2ecf20Sopenharmony_ci if (ret > 0 && copy_to_user(buf, rp->data, ret)) 22028c2ecf20Sopenharmony_ci ret = -EFAULT; 22038c2ecf20Sopenharmony_ci if (++i >= RB_SIZE) 22048c2ecf20Sopenharmony_ci i = 0; 22058c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 22068c2ecf20Sopenharmony_ci pp->rb_get = i; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci if (ret >= 0) 22098c2ecf20Sopenharmony_ci break; 22108c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 22118c2ecf20Sopenharmony_ci break; 22128c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 22138c2ecf20Sopenharmony_ci if (signal_pending(current)) 22148c2ecf20Sopenharmony_ci break; 22158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 22168c2ecf20Sopenharmony_ci schedule(); 22178c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 22208c2ecf20Sopenharmony_ci remove_wait_queue(&pp->wait, &wait); 22218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci return ret; 22248c2ecf20Sopenharmony_ci} 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_cistatic ssize_t 22278c2ecf20Sopenharmony_cipmu_write(struct file *file, const char __user *buf, 22288c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci return 0; 22318c2ecf20Sopenharmony_ci} 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_cistatic __poll_t 22348c2ecf20Sopenharmony_cipmu_fpoll(struct file *filp, poll_table *wait) 22358c2ecf20Sopenharmony_ci{ 22368c2ecf20Sopenharmony_ci struct pmu_private *pp = filp->private_data; 22378c2ecf20Sopenharmony_ci __poll_t mask = 0; 22388c2ecf20Sopenharmony_ci unsigned long flags; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci if (!pp) 22418c2ecf20Sopenharmony_ci return 0; 22428c2ecf20Sopenharmony_ci poll_wait(filp, &pp->wait, wait); 22438c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 22448c2ecf20Sopenharmony_ci if (pp->rb_get != pp->rb_put) 22458c2ecf20Sopenharmony_ci mask |= EPOLLIN; 22468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 22478c2ecf20Sopenharmony_ci return mask; 22488c2ecf20Sopenharmony_ci} 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_cistatic int 22518c2ecf20Sopenharmony_cipmu_release(struct inode *inode, struct file *file) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci struct pmu_private *pp = file->private_data; 22548c2ecf20Sopenharmony_ci unsigned long flags; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci if (pp) { 22578c2ecf20Sopenharmony_ci file->private_data = NULL; 22588c2ecf20Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 22598c2ecf20Sopenharmony_ci list_del(&pp->list); 22608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 22638c2ecf20Sopenharmony_ci if (pp->backlight_locker) 22648c2ecf20Sopenharmony_ci pmac_backlight_enable(); 22658c2ecf20Sopenharmony_ci#endif 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci kfree(pp); 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci return 0; 22708c2ecf20Sopenharmony_ci} 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 22738c2ecf20Sopenharmony_cistatic void pmac_suspend_disable_irqs(void) 22748c2ecf20Sopenharmony_ci{ 22758c2ecf20Sopenharmony_ci /* Call platform functions marked "on sleep" */ 22768c2ecf20Sopenharmony_ci pmac_pfunc_i2c_suspend(); 22778c2ecf20Sopenharmony_ci pmac_pfunc_base_suspend(); 22788c2ecf20Sopenharmony_ci} 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_cistatic int powerbook_sleep(suspend_state_t state) 22818c2ecf20Sopenharmony_ci{ 22828c2ecf20Sopenharmony_ci int error = 0; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci /* Wait for completion of async requests */ 22858c2ecf20Sopenharmony_ci while (!batt_req.complete) 22868c2ecf20Sopenharmony_ci pmu_poll(); 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci /* Giveup the lazy FPU & vec so we don't have to back them 22898c2ecf20Sopenharmony_ci * up from the low level code 22908c2ecf20Sopenharmony_ci */ 22918c2ecf20Sopenharmony_ci enable_kernel_fp(); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci#ifdef CONFIG_ALTIVEC 22948c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ALTIVEC)) 22958c2ecf20Sopenharmony_ci enable_kernel_altivec(); 22968c2ecf20Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci switch (pmu_kind) { 22998c2ecf20Sopenharmony_ci case PMU_OHARE_BASED: 23008c2ecf20Sopenharmony_ci error = powerbook_sleep_3400(); 23018c2ecf20Sopenharmony_ci break; 23028c2ecf20Sopenharmony_ci case PMU_HEATHROW_BASED: 23038c2ecf20Sopenharmony_ci case PMU_PADDINGTON_BASED: 23048c2ecf20Sopenharmony_ci error = powerbook_sleep_grackle(); 23058c2ecf20Sopenharmony_ci break; 23068c2ecf20Sopenharmony_ci case PMU_KEYLARGO_BASED: 23078c2ecf20Sopenharmony_ci error = powerbook_sleep_Core99(); 23088c2ecf20Sopenharmony_ci break; 23098c2ecf20Sopenharmony_ci default: 23108c2ecf20Sopenharmony_ci return -ENOSYS; 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci if (error) 23148c2ecf20Sopenharmony_ci return error; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci mdelay(100); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci return 0; 23198c2ecf20Sopenharmony_ci} 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_cistatic void pmac_suspend_enable_irqs(void) 23228c2ecf20Sopenharmony_ci{ 23238c2ecf20Sopenharmony_ci /* Force a poll of ADB interrupts */ 23248c2ecf20Sopenharmony_ci adb_int_pending = 1; 23258c2ecf20Sopenharmony_ci via_pmu_interrupt(0, NULL); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci mdelay(10); 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci /* Call platform functions marked "on wake" */ 23308c2ecf20Sopenharmony_ci pmac_pfunc_base_resume(); 23318c2ecf20Sopenharmony_ci pmac_pfunc_i2c_resume(); 23328c2ecf20Sopenharmony_ci} 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_cistatic int pmu_sleep_valid(suspend_state_t state) 23358c2ecf20Sopenharmony_ci{ 23368c2ecf20Sopenharmony_ci return state == PM_SUSPEND_MEM 23378c2ecf20Sopenharmony_ci && (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0); 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops pmu_pm_ops = { 23418c2ecf20Sopenharmony_ci .enter = powerbook_sleep, 23428c2ecf20Sopenharmony_ci .valid = pmu_sleep_valid, 23438c2ecf20Sopenharmony_ci}; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_cistatic int register_pmu_pm_ops(void) 23468c2ecf20Sopenharmony_ci{ 23478c2ecf20Sopenharmony_ci if (pmu_kind == PMU_OHARE_BASED) 23488c2ecf20Sopenharmony_ci powerbook_sleep_init_3400(); 23498c2ecf20Sopenharmony_ci ppc_md.suspend_disable_irqs = pmac_suspend_disable_irqs; 23508c2ecf20Sopenharmony_ci ppc_md.suspend_enable_irqs = pmac_suspend_enable_irqs; 23518c2ecf20Sopenharmony_ci suspend_set_ops(&pmu_pm_ops); 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci return 0; 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_cidevice_initcall(register_pmu_pm_ops); 23578c2ecf20Sopenharmony_ci#endif 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_cistatic int pmu_ioctl(struct file *filp, 23608c2ecf20Sopenharmony_ci u_int cmd, u_long arg) 23618c2ecf20Sopenharmony_ci{ 23628c2ecf20Sopenharmony_ci __u32 __user *argp = (__u32 __user *)arg; 23638c2ecf20Sopenharmony_ci int error = -EINVAL; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci switch (cmd) { 23668c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 23678c2ecf20Sopenharmony_ci case PMU_IOC_SLEEP: 23688c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 23698c2ecf20Sopenharmony_ci return -EACCES; 23708c2ecf20Sopenharmony_ci return pm_suspend(PM_SUSPEND_MEM); 23718c2ecf20Sopenharmony_ci case PMU_IOC_CAN_SLEEP: 23728c2ecf20Sopenharmony_ci if (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) < 0) 23738c2ecf20Sopenharmony_ci return put_user(0, argp); 23748c2ecf20Sopenharmony_ci else 23758c2ecf20Sopenharmony_ci return put_user(1, argp); 23768c2ecf20Sopenharmony_ci#endif 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY 23798c2ecf20Sopenharmony_ci /* Compatibility ioctl's for backlight */ 23808c2ecf20Sopenharmony_ci case PMU_IOC_GET_BACKLIGHT: 23818c2ecf20Sopenharmony_ci { 23828c2ecf20Sopenharmony_ci int brightness; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci brightness = pmac_backlight_get_legacy_brightness(); 23858c2ecf20Sopenharmony_ci if (brightness < 0) 23868c2ecf20Sopenharmony_ci return brightness; 23878c2ecf20Sopenharmony_ci else 23888c2ecf20Sopenharmony_ci return put_user(brightness, argp); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci case PMU_IOC_SET_BACKLIGHT: 23928c2ecf20Sopenharmony_ci { 23938c2ecf20Sopenharmony_ci int brightness; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci error = get_user(brightness, argp); 23968c2ecf20Sopenharmony_ci if (error) 23978c2ecf20Sopenharmony_ci return error; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci return pmac_backlight_set_legacy_brightness(brightness); 24008c2ecf20Sopenharmony_ci } 24018c2ecf20Sopenharmony_ci#ifdef CONFIG_INPUT_ADBHID 24028c2ecf20Sopenharmony_ci case PMU_IOC_GRAB_BACKLIGHT: { 24038c2ecf20Sopenharmony_ci struct pmu_private *pp = filp->private_data; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (pp->backlight_locker) 24068c2ecf20Sopenharmony_ci return 0; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci pp->backlight_locker = 1; 24098c2ecf20Sopenharmony_ci pmac_backlight_disable(); 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci return 0; 24128c2ecf20Sopenharmony_ci } 24138c2ecf20Sopenharmony_ci#endif /* CONFIG_INPUT_ADBHID */ 24148c2ecf20Sopenharmony_ci#endif /* CONFIG_PMAC_BACKLIGHT_LEGACY */ 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci case PMU_IOC_GET_MODEL: 24178c2ecf20Sopenharmony_ci return put_user(pmu_kind, argp); 24188c2ecf20Sopenharmony_ci case PMU_IOC_HAS_ADB: 24198c2ecf20Sopenharmony_ci return put_user(pmu_has_adb, argp); 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci return error; 24228c2ecf20Sopenharmony_ci} 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_cistatic long pmu_unlocked_ioctl(struct file *filp, 24258c2ecf20Sopenharmony_ci u_int cmd, u_long arg) 24268c2ecf20Sopenharmony_ci{ 24278c2ecf20Sopenharmony_ci int ret; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci mutex_lock(&pmu_info_proc_mutex); 24308c2ecf20Sopenharmony_ci ret = pmu_ioctl(filp, cmd, arg); 24318c2ecf20Sopenharmony_ci mutex_unlock(&pmu_info_proc_mutex); 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci return ret; 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 24378c2ecf20Sopenharmony_ci#define PMU_IOC_GET_BACKLIGHT32 _IOR('B', 1, compat_size_t) 24388c2ecf20Sopenharmony_ci#define PMU_IOC_SET_BACKLIGHT32 _IOW('B', 2, compat_size_t) 24398c2ecf20Sopenharmony_ci#define PMU_IOC_GET_MODEL32 _IOR('B', 3, compat_size_t) 24408c2ecf20Sopenharmony_ci#define PMU_IOC_HAS_ADB32 _IOR('B', 4, compat_size_t) 24418c2ecf20Sopenharmony_ci#define PMU_IOC_CAN_SLEEP32 _IOR('B', 5, compat_size_t) 24428c2ecf20Sopenharmony_ci#define PMU_IOC_GRAB_BACKLIGHT32 _IOR('B', 6, compat_size_t) 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistatic long compat_pmu_ioctl (struct file *filp, u_int cmd, u_long arg) 24458c2ecf20Sopenharmony_ci{ 24468c2ecf20Sopenharmony_ci switch (cmd) { 24478c2ecf20Sopenharmony_ci case PMU_IOC_SLEEP: 24488c2ecf20Sopenharmony_ci break; 24498c2ecf20Sopenharmony_ci case PMU_IOC_GET_BACKLIGHT32: 24508c2ecf20Sopenharmony_ci cmd = PMU_IOC_GET_BACKLIGHT; 24518c2ecf20Sopenharmony_ci break; 24528c2ecf20Sopenharmony_ci case PMU_IOC_SET_BACKLIGHT32: 24538c2ecf20Sopenharmony_ci cmd = PMU_IOC_SET_BACKLIGHT; 24548c2ecf20Sopenharmony_ci break; 24558c2ecf20Sopenharmony_ci case PMU_IOC_GET_MODEL32: 24568c2ecf20Sopenharmony_ci cmd = PMU_IOC_GET_MODEL; 24578c2ecf20Sopenharmony_ci break; 24588c2ecf20Sopenharmony_ci case PMU_IOC_HAS_ADB32: 24598c2ecf20Sopenharmony_ci cmd = PMU_IOC_HAS_ADB; 24608c2ecf20Sopenharmony_ci break; 24618c2ecf20Sopenharmony_ci case PMU_IOC_CAN_SLEEP32: 24628c2ecf20Sopenharmony_ci cmd = PMU_IOC_CAN_SLEEP; 24638c2ecf20Sopenharmony_ci break; 24648c2ecf20Sopenharmony_ci case PMU_IOC_GRAB_BACKLIGHT32: 24658c2ecf20Sopenharmony_ci cmd = PMU_IOC_GRAB_BACKLIGHT; 24668c2ecf20Sopenharmony_ci break; 24678c2ecf20Sopenharmony_ci default: 24688c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 24698c2ecf20Sopenharmony_ci } 24708c2ecf20Sopenharmony_ci return pmu_unlocked_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); 24718c2ecf20Sopenharmony_ci} 24728c2ecf20Sopenharmony_ci#endif 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic const struct file_operations pmu_device_fops = { 24758c2ecf20Sopenharmony_ci .read = pmu_read, 24768c2ecf20Sopenharmony_ci .write = pmu_write, 24778c2ecf20Sopenharmony_ci .poll = pmu_fpoll, 24788c2ecf20Sopenharmony_ci .unlocked_ioctl = pmu_unlocked_ioctl, 24798c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 24808c2ecf20Sopenharmony_ci .compat_ioctl = compat_pmu_ioctl, 24818c2ecf20Sopenharmony_ci#endif 24828c2ecf20Sopenharmony_ci .open = pmu_open, 24838c2ecf20Sopenharmony_ci .release = pmu_release, 24848c2ecf20Sopenharmony_ci .llseek = noop_llseek, 24858c2ecf20Sopenharmony_ci}; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_cistatic struct miscdevice pmu_device = { 24888c2ecf20Sopenharmony_ci PMU_MINOR, "pmu", &pmu_device_fops 24898c2ecf20Sopenharmony_ci}; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_cistatic int pmu_device_init(void) 24928c2ecf20Sopenharmony_ci{ 24938c2ecf20Sopenharmony_ci if (pmu_state == uninitialized) 24948c2ecf20Sopenharmony_ci return 0; 24958c2ecf20Sopenharmony_ci if (misc_register(&pmu_device) < 0) 24968c2ecf20Sopenharmony_ci printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 24978c2ecf20Sopenharmony_ci return 0; 24988c2ecf20Sopenharmony_ci} 24998c2ecf20Sopenharmony_cidevice_initcall(pmu_device_init); 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci#ifdef DEBUG_SLEEP 25038c2ecf20Sopenharmony_cistatic inline void 25048c2ecf20Sopenharmony_cipolled_handshake(void) 25058c2ecf20Sopenharmony_ci{ 25068c2ecf20Sopenharmony_ci via2[B] &= ~TREQ; eieio(); 25078c2ecf20Sopenharmony_ci while ((via2[B] & TACK) != 0) 25088c2ecf20Sopenharmony_ci ; 25098c2ecf20Sopenharmony_ci via2[B] |= TREQ; eieio(); 25108c2ecf20Sopenharmony_ci while ((via2[B] & TACK) == 0) 25118c2ecf20Sopenharmony_ci ; 25128c2ecf20Sopenharmony_ci} 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_cistatic inline void 25158c2ecf20Sopenharmony_cipolled_send_byte(int x) 25168c2ecf20Sopenharmony_ci{ 25178c2ecf20Sopenharmony_ci via1[ACR] |= SR_OUT | SR_EXT; eieio(); 25188c2ecf20Sopenharmony_ci via1[SR] = x; eieio(); 25198c2ecf20Sopenharmony_ci polled_handshake(); 25208c2ecf20Sopenharmony_ci} 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_cistatic inline int 25238c2ecf20Sopenharmony_cipolled_recv_byte(void) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci int x; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci via1[ACR] = (via1[ACR] & ~SR_OUT) | SR_EXT; eieio(); 25288c2ecf20Sopenharmony_ci x = via1[SR]; eieio(); 25298c2ecf20Sopenharmony_ci polled_handshake(); 25308c2ecf20Sopenharmony_ci x = via1[SR]; eieio(); 25318c2ecf20Sopenharmony_ci return x; 25328c2ecf20Sopenharmony_ci} 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ciint 25358c2ecf20Sopenharmony_cipmu_polled_request(struct adb_request *req) 25368c2ecf20Sopenharmony_ci{ 25378c2ecf20Sopenharmony_ci unsigned long flags; 25388c2ecf20Sopenharmony_ci int i, l, c; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci req->complete = 1; 25418c2ecf20Sopenharmony_ci c = req->data[0]; 25428c2ecf20Sopenharmony_ci l = pmu_data_len[c][0]; 25438c2ecf20Sopenharmony_ci if (l >= 0 && req->nbytes != l + 1) 25448c2ecf20Sopenharmony_ci return -EINVAL; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci local_irq_save(flags); 25478c2ecf20Sopenharmony_ci while (pmu_state != idle) 25488c2ecf20Sopenharmony_ci pmu_poll(); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci while ((via2[B] & TACK) == 0) 25518c2ecf20Sopenharmony_ci ; 25528c2ecf20Sopenharmony_ci polled_send_byte(c); 25538c2ecf20Sopenharmony_ci if (l < 0) { 25548c2ecf20Sopenharmony_ci l = req->nbytes - 1; 25558c2ecf20Sopenharmony_ci polled_send_byte(l); 25568c2ecf20Sopenharmony_ci } 25578c2ecf20Sopenharmony_ci for (i = 1; i <= l; ++i) 25588c2ecf20Sopenharmony_ci polled_send_byte(req->data[i]); 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci l = pmu_data_len[c][1]; 25618c2ecf20Sopenharmony_ci if (l < 0) 25628c2ecf20Sopenharmony_ci l = polled_recv_byte(); 25638c2ecf20Sopenharmony_ci for (i = 0; i < l; ++i) 25648c2ecf20Sopenharmony_ci req->reply[i + req->reply_len] = polled_recv_byte(); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci if (req->done) 25678c2ecf20Sopenharmony_ci (*req->done)(req); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci local_irq_restore(flags); 25708c2ecf20Sopenharmony_ci return 0; 25718c2ecf20Sopenharmony_ci} 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci/* N.B. This doesn't work on the 3400 */ 25748c2ecf20Sopenharmony_civoid pmu_blink(int n) 25758c2ecf20Sopenharmony_ci{ 25768c2ecf20Sopenharmony_ci struct adb_request req; 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci for (; n > 0; --n) { 25818c2ecf20Sopenharmony_ci req.nbytes = 4; 25828c2ecf20Sopenharmony_ci req.done = NULL; 25838c2ecf20Sopenharmony_ci req.data[0] = 0xee; 25848c2ecf20Sopenharmony_ci req.data[1] = 4; 25858c2ecf20Sopenharmony_ci req.data[2] = 0; 25868c2ecf20Sopenharmony_ci req.data[3] = 1; 25878c2ecf20Sopenharmony_ci req.reply[0] = ADB_RET_OK; 25888c2ecf20Sopenharmony_ci req.reply_len = 1; 25898c2ecf20Sopenharmony_ci req.reply_expected = 0; 25908c2ecf20Sopenharmony_ci pmu_polled_request(&req); 25918c2ecf20Sopenharmony_ci mdelay(50); 25928c2ecf20Sopenharmony_ci req.nbytes = 4; 25938c2ecf20Sopenharmony_ci req.done = NULL; 25948c2ecf20Sopenharmony_ci req.data[0] = 0xee; 25958c2ecf20Sopenharmony_ci req.data[1] = 4; 25968c2ecf20Sopenharmony_ci req.data[2] = 0; 25978c2ecf20Sopenharmony_ci req.data[3] = 0; 25988c2ecf20Sopenharmony_ci req.reply[0] = ADB_RET_OK; 25998c2ecf20Sopenharmony_ci req.reply_len = 1; 26008c2ecf20Sopenharmony_ci req.reply_expected = 0; 26018c2ecf20Sopenharmony_ci pmu_polled_request(&req); 26028c2ecf20Sopenharmony_ci mdelay(50); 26038c2ecf20Sopenharmony_ci } 26048c2ecf20Sopenharmony_ci mdelay(50); 26058c2ecf20Sopenharmony_ci} 26068c2ecf20Sopenharmony_ci#endif /* DEBUG_SLEEP */ 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 26098c2ecf20Sopenharmony_ciint pmu_sys_suspended; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_cistatic int pmu_syscore_suspend(void) 26128c2ecf20Sopenharmony_ci{ 26138c2ecf20Sopenharmony_ci /* Suspend PMU event interrupts */ 26148c2ecf20Sopenharmony_ci pmu_suspend(); 26158c2ecf20Sopenharmony_ci pmu_sys_suspended = 1; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 26188c2ecf20Sopenharmony_ci /* Tell backlight code not to muck around with the chip anymore */ 26198c2ecf20Sopenharmony_ci pmu_backlight_set_sleep(1); 26208c2ecf20Sopenharmony_ci#endif 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci return 0; 26238c2ecf20Sopenharmony_ci} 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_cistatic void pmu_syscore_resume(void) 26268c2ecf20Sopenharmony_ci{ 26278c2ecf20Sopenharmony_ci struct adb_request req; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci if (!pmu_sys_suspended) 26308c2ecf20Sopenharmony_ci return; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci /* Tell PMU we are ready */ 26338c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 26348c2ecf20Sopenharmony_ci pmu_wait_complete(&req); 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 26378c2ecf20Sopenharmony_ci /* Tell backlight code it can use the chip again */ 26388c2ecf20Sopenharmony_ci pmu_backlight_set_sleep(0); 26398c2ecf20Sopenharmony_ci#endif 26408c2ecf20Sopenharmony_ci /* Resume PMU event interrupts */ 26418c2ecf20Sopenharmony_ci pmu_resume(); 26428c2ecf20Sopenharmony_ci pmu_sys_suspended = 0; 26438c2ecf20Sopenharmony_ci} 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_cistatic struct syscore_ops pmu_syscore_ops = { 26468c2ecf20Sopenharmony_ci .suspend = pmu_syscore_suspend, 26478c2ecf20Sopenharmony_ci .resume = pmu_syscore_resume, 26488c2ecf20Sopenharmony_ci}; 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_cistatic int pmu_syscore_register(void) 26518c2ecf20Sopenharmony_ci{ 26528c2ecf20Sopenharmony_ci register_syscore_ops(&pmu_syscore_ops); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci return 0; 26558c2ecf20Sopenharmony_ci} 26568c2ecf20Sopenharmony_cisubsys_initcall(pmu_syscore_register); 26578c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_request); 26608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_queue_request); 26618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_poll); 26628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_poll_adb); 26638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_wait_complete); 26648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_suspend); 26658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_resume); 26668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_unlock); 26678c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC32) 26688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_enable_irled); 26698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_battery_count); 26708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_batteries); 26718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmu_power_flags); 26728c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 26738c2ecf20Sopenharmony_ci 2674