162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for the PMU in Apple PowerBooks and PowerMacs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The VIA (versatile interface adapter) interfaces to the PMU, 662306a36Sopenharmony_ci * a 6805 microprocessor core whose primary function is to control 762306a36Sopenharmony_ci * battery charging and system power on the PowerBook 3400 and 2400. 862306a36Sopenharmony_ci * The PMU also controls the ADB (Apple Desktop Bus) which connects 962306a36Sopenharmony_ci * to the keyboard and mouse, as well as the non-volatile RAM 1062306a36Sopenharmony_ci * and the RTC (real time clock) chip. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. 1362306a36Sopenharmony_ci * Copyright (C) 2001-2002 Benjamin Herrenschmidt 1462306a36Sopenharmony_ci * Copyright (C) 2006-2007 Johannes Berg 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * THIS DRIVER IS BECOMING A TOTAL MESS ! 1762306a36Sopenharmony_ci * - Cleanup atomically disabling reply to PMU events after 1862306a36Sopenharmony_ci * a sleep or a freq. switch 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <linux/stdarg.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/errno.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/sched/signal.h> 2862306a36Sopenharmony_ci#include <linux/miscdevice.h> 2962306a36Sopenharmony_ci#include <linux/blkdev.h> 3062306a36Sopenharmony_ci#include <linux/pci.h> 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci#include <linux/poll.h> 3362306a36Sopenharmony_ci#include <linux/adb.h> 3462306a36Sopenharmony_ci#include <linux/pmu.h> 3562306a36Sopenharmony_ci#include <linux/cuda.h> 3662306a36Sopenharmony_ci#include <linux/module.h> 3762306a36Sopenharmony_ci#include <linux/spinlock.h> 3862306a36Sopenharmony_ci#include <linux/pm.h> 3962306a36Sopenharmony_ci#include <linux/proc_fs.h> 4062306a36Sopenharmony_ci#include <linux/seq_file.h> 4162306a36Sopenharmony_ci#include <linux/init.h> 4262306a36Sopenharmony_ci#include <linux/interrupt.h> 4362306a36Sopenharmony_ci#include <linux/device.h> 4462306a36Sopenharmony_ci#include <linux/syscore_ops.h> 4562306a36Sopenharmony_ci#include <linux/freezer.h> 4662306a36Sopenharmony_ci#include <linux/syscalls.h> 4762306a36Sopenharmony_ci#include <linux/suspend.h> 4862306a36Sopenharmony_ci#include <linux/cpu.h> 4962306a36Sopenharmony_ci#include <linux/compat.h> 5062306a36Sopenharmony_ci#include <linux/of_address.h> 5162306a36Sopenharmony_ci#include <linux/of_irq.h> 5262306a36Sopenharmony_ci#include <linux/uaccess.h> 5362306a36Sopenharmony_ci#include <linux/pgtable.h> 5462306a36Sopenharmony_ci#include <asm/machdep.h> 5562306a36Sopenharmony_ci#include <asm/io.h> 5662306a36Sopenharmony_ci#include <asm/sections.h> 5762306a36Sopenharmony_ci#include <asm/irq.h> 5862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 5962306a36Sopenharmony_ci#include <asm/pmac_feature.h> 6062306a36Sopenharmony_ci#include <asm/pmac_pfunc.h> 6162306a36Sopenharmony_ci#include <asm/pmac_low_i2c.h> 6262306a36Sopenharmony_ci#include <asm/mmu_context.h> 6362306a36Sopenharmony_ci#include <asm/cputable.h> 6462306a36Sopenharmony_ci#include <asm/time.h> 6562306a36Sopenharmony_ci#include <asm/backlight.h> 6662306a36Sopenharmony_ci#else 6762306a36Sopenharmony_ci#include <asm/macintosh.h> 6862306a36Sopenharmony_ci#include <asm/macints.h> 6962306a36Sopenharmony_ci#include <asm/mac_via.h> 7062306a36Sopenharmony_ci#endif 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#include "via-pmu-event.h" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Some compile options */ 7562306a36Sopenharmony_ci#undef DEBUG_SLEEP 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* How many iterations between battery polls */ 7862306a36Sopenharmony_ci#define BATTERY_POLLING_COUNT 2 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic DEFINE_MUTEX(pmu_info_proc_mutex); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* VIA registers - spaced 0x200 bytes apart */ 8362306a36Sopenharmony_ci#define RS 0x200 /* skip between registers */ 8462306a36Sopenharmony_ci#define B 0 /* B-side data */ 8562306a36Sopenharmony_ci#define A RS /* A-side data */ 8662306a36Sopenharmony_ci#define DIRB (2*RS) /* B-side direction (1=output) */ 8762306a36Sopenharmony_ci#define DIRA (3*RS) /* A-side direction (1=output) */ 8862306a36Sopenharmony_ci#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ 8962306a36Sopenharmony_ci#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ 9062306a36Sopenharmony_ci#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ 9162306a36Sopenharmony_ci#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ 9262306a36Sopenharmony_ci#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ 9362306a36Sopenharmony_ci#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ 9462306a36Sopenharmony_ci#define SR (10*RS) /* Shift register */ 9562306a36Sopenharmony_ci#define ACR (11*RS) /* Auxiliary control register */ 9662306a36Sopenharmony_ci#define PCR (12*RS) /* Peripheral control register */ 9762306a36Sopenharmony_ci#define IFR (13*RS) /* Interrupt flag register */ 9862306a36Sopenharmony_ci#define IER (14*RS) /* Interrupt enable register */ 9962306a36Sopenharmony_ci#define ANH (15*RS) /* A-side data, no handshake */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Bits in B data register: both active low */ 10262306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 10362306a36Sopenharmony_ci#define TACK 0x08 /* Transfer acknowledge (input) */ 10462306a36Sopenharmony_ci#define TREQ 0x10 /* Transfer request (output) */ 10562306a36Sopenharmony_ci#else 10662306a36Sopenharmony_ci#define TACK 0x02 10762306a36Sopenharmony_ci#define TREQ 0x04 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* Bits in ACR */ 11162306a36Sopenharmony_ci#define SR_CTRL 0x1c /* Shift register control bits */ 11262306a36Sopenharmony_ci#define SR_EXT 0x0c /* Shift on external clock */ 11362306a36Sopenharmony_ci#define SR_OUT 0x10 /* Shift out if 1 */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Bits in IFR and IER */ 11662306a36Sopenharmony_ci#define IER_SET 0x80 /* set bits in IER */ 11762306a36Sopenharmony_ci#define IER_CLR 0 /* clear bits in IER */ 11862306a36Sopenharmony_ci#define SR_INT 0x04 /* Shift register full/empty */ 11962306a36Sopenharmony_ci#define CB2_INT 0x08 12062306a36Sopenharmony_ci#define CB1_INT 0x10 /* transition on CB1 input */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic volatile enum pmu_state { 12362306a36Sopenharmony_ci uninitialized = 0, 12462306a36Sopenharmony_ci idle, 12562306a36Sopenharmony_ci sending, 12662306a36Sopenharmony_ci intack, 12762306a36Sopenharmony_ci reading, 12862306a36Sopenharmony_ci reading_intr, 12962306a36Sopenharmony_ci locked, 13062306a36Sopenharmony_ci} pmu_state; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic volatile enum int_data_state { 13362306a36Sopenharmony_ci int_data_empty, 13462306a36Sopenharmony_ci int_data_fill, 13562306a36Sopenharmony_ci int_data_ready, 13662306a36Sopenharmony_ci int_data_flush 13762306a36Sopenharmony_ci} int_data_state[2] = { int_data_empty, int_data_empty }; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct adb_request *current_req; 14062306a36Sopenharmony_cistatic struct adb_request *last_req; 14162306a36Sopenharmony_cistatic struct adb_request *req_awaiting_reply; 14262306a36Sopenharmony_cistatic unsigned char interrupt_data[2][32]; 14362306a36Sopenharmony_cistatic int interrupt_data_len[2]; 14462306a36Sopenharmony_cistatic int int_data_last; 14562306a36Sopenharmony_cistatic unsigned char *reply_ptr; 14662306a36Sopenharmony_cistatic int data_index; 14762306a36Sopenharmony_cistatic int data_len; 14862306a36Sopenharmony_cistatic volatile int adb_int_pending; 14962306a36Sopenharmony_cistatic volatile int disable_poll; 15062306a36Sopenharmony_cistatic int pmu_kind = PMU_UNKNOWN; 15162306a36Sopenharmony_cistatic int pmu_fully_inited; 15262306a36Sopenharmony_cistatic int pmu_has_adb; 15362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 15462306a36Sopenharmony_cistatic volatile unsigned char __iomem *via1; 15562306a36Sopenharmony_cistatic volatile unsigned char __iomem *via2; 15662306a36Sopenharmony_cistatic struct device_node *vias; 15762306a36Sopenharmony_cistatic struct device_node *gpio_node; 15862306a36Sopenharmony_ci#endif 15962306a36Sopenharmony_cistatic unsigned char __iomem *gpio_reg; 16062306a36Sopenharmony_cistatic int gpio_irq = 0; 16162306a36Sopenharmony_cistatic int gpio_irq_enabled = -1; 16262306a36Sopenharmony_cistatic volatile int pmu_suspended; 16362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pmu_lock); 16462306a36Sopenharmony_cistatic u8 pmu_intr_mask; 16562306a36Sopenharmony_cistatic int pmu_version; 16662306a36Sopenharmony_cistatic int drop_interrupts; 16762306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 16862306a36Sopenharmony_cistatic int option_lid_wakeup = 1; 16962306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 17062306a36Sopenharmony_cistatic unsigned long async_req_locks; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define NUM_IRQ_STATS 13 17362306a36Sopenharmony_cistatic unsigned int pmu_irq_stats[NUM_IRQ_STATS]; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_root; 17662306a36Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_info; 17762306a36Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_irqstats; 17862306a36Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_options; 17962306a36Sopenharmony_cistatic int option_server_mode; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciint pmu_battery_count; 18262306a36Sopenharmony_cistatic int pmu_cur_battery; 18362306a36Sopenharmony_ciunsigned int pmu_power_flags = PMU_PWR_AC_PRESENT; 18462306a36Sopenharmony_cistruct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; 18562306a36Sopenharmony_cistatic int query_batt_timer = BATTERY_POLLING_COUNT; 18662306a36Sopenharmony_cistatic struct adb_request batt_req; 18762306a36Sopenharmony_cistatic struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES]; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint asleep; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#ifdef CONFIG_ADB 19262306a36Sopenharmony_cistatic int adb_dev_map; 19362306a36Sopenharmony_cistatic int pmu_adb_flags; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int pmu_probe(void); 19662306a36Sopenharmony_cistatic int pmu_init(void); 19762306a36Sopenharmony_cistatic int pmu_send_request(struct adb_request *req, int sync); 19862306a36Sopenharmony_cistatic int pmu_adb_autopoll(int devs); 19962306a36Sopenharmony_cistatic int pmu_adb_reset_bus(void); 20062306a36Sopenharmony_ci#endif /* CONFIG_ADB */ 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int init_pmu(void); 20362306a36Sopenharmony_cistatic void pmu_start(void); 20462306a36Sopenharmony_cistatic irqreturn_t via_pmu_interrupt(int irq, void *arg); 20562306a36Sopenharmony_cistatic irqreturn_t gpio1_interrupt(int irq, void *arg); 20662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 20762306a36Sopenharmony_cistatic int pmu_info_proc_show(struct seq_file *m, void *v); 20862306a36Sopenharmony_cistatic int pmu_irqstats_proc_show(struct seq_file *m, void *v); 20962306a36Sopenharmony_cistatic int pmu_battery_proc_show(struct seq_file *m, void *v); 21062306a36Sopenharmony_ci#endif 21162306a36Sopenharmony_cistatic void pmu_pass_intr(unsigned char *data, int len); 21262306a36Sopenharmony_cistatic const struct proc_ops pmu_options_proc_ops; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#ifdef CONFIG_ADB 21562306a36Sopenharmony_ciconst struct adb_driver via_pmu_driver = { 21662306a36Sopenharmony_ci .name = "PMU", 21762306a36Sopenharmony_ci .probe = pmu_probe, 21862306a36Sopenharmony_ci .init = pmu_init, 21962306a36Sopenharmony_ci .send_request = pmu_send_request, 22062306a36Sopenharmony_ci .autopoll = pmu_adb_autopoll, 22162306a36Sopenharmony_ci .poll = pmu_poll_adb, 22262306a36Sopenharmony_ci .reset_bus = pmu_adb_reset_bus, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci#endif /* CONFIG_ADB */ 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciextern void low_sleep_handler(void); 22762306a36Sopenharmony_ciextern void enable_kernel_altivec(void); 22862306a36Sopenharmony_ciextern void enable_kernel_fp(void); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#ifdef DEBUG_SLEEP 23162306a36Sopenharmony_ciint pmu_polled_request(struct adb_request *req); 23262306a36Sopenharmony_civoid pmu_blink(int n); 23362306a36Sopenharmony_ci#endif 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * This table indicates for each PMU opcode: 23762306a36Sopenharmony_ci * - the number of data bytes to be sent with the command, or -1 23862306a36Sopenharmony_ci * if a length byte should be sent, 23962306a36Sopenharmony_ci * - the number of response bytes which the PMU will return, or 24062306a36Sopenharmony_ci * -1 if it will send a length byte. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic const s8 pmu_data_len[256][2] = { 24362306a36Sopenharmony_ci/* 0 1 2 3 4 5 6 7 */ 24462306a36Sopenharmony_ci/*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 24562306a36Sopenharmony_ci/*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 24662306a36Sopenharmony_ci/*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 24762306a36Sopenharmony_ci/*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0}, 24862306a36Sopenharmony_ci/*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, 24962306a36Sopenharmony_ci/*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1}, 25062306a36Sopenharmony_ci/*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 25162306a36Sopenharmony_ci/*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0}, 25262306a36Sopenharmony_ci/*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 25362306a36Sopenharmony_ci/*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1}, 25462306a36Sopenharmony_ci/*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, 25562306a36Sopenharmony_ci/*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1}, 25662306a36Sopenharmony_ci/*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 25762306a36Sopenharmony_ci/*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1}, 25862306a36Sopenharmony_ci/*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 25962306a36Sopenharmony_ci/*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1}, 26062306a36Sopenharmony_ci/*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 26162306a36Sopenharmony_ci/*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 26262306a36Sopenharmony_ci/*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 26362306a36Sopenharmony_ci/*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 26462306a36Sopenharmony_ci/*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, 26562306a36Sopenharmony_ci/*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 26662306a36Sopenharmony_ci/*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 26762306a36Sopenharmony_ci/*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 26862306a36Sopenharmony_ci/*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 26962306a36Sopenharmony_ci/*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 27062306a36Sopenharmony_ci/*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 27162306a36Sopenharmony_ci/*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1}, 27262306a36Sopenharmony_ci/*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, 27362306a36Sopenharmony_ci/*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0}, 27462306a36Sopenharmony_ci/*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 27562306a36Sopenharmony_ci/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic char *pbook_type[] = { 27962306a36Sopenharmony_ci "Unknown PowerBook", 28062306a36Sopenharmony_ci "PowerBook 2400/3400/3500(G3)", 28162306a36Sopenharmony_ci "PowerBook G3 Series", 28262306a36Sopenharmony_ci "1999 PowerBook G3", 28362306a36Sopenharmony_ci "Core99" 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciint __init find_via_pmu(void) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 28962306a36Sopenharmony_ci int err; 29062306a36Sopenharmony_ci u64 taddr; 29162306a36Sopenharmony_ci struct resource res; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (pmu_state != uninitialized) 29462306a36Sopenharmony_ci return 1; 29562306a36Sopenharmony_ci vias = of_find_node_by_name(NULL, "via-pmu"); 29662306a36Sopenharmony_ci if (vias == NULL) 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci err = of_address_to_resource(vias, 0, &res); 30062306a36Sopenharmony_ci if (err) { 30162306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: Error getting \"reg\" property !\n"); 30262306a36Sopenharmony_ci goto fail; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci taddr = res.start; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci pmu_has_adb = 1; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 30962306a36Sopenharmony_ci PMU_INT_SNDBRT | 31062306a36Sopenharmony_ci PMU_INT_ADB | 31162306a36Sopenharmony_ci PMU_INT_TICK; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (of_node_name_eq(vias->parent, "ohare") || 31462306a36Sopenharmony_ci of_device_is_compatible(vias->parent, "ohare")) 31562306a36Sopenharmony_ci pmu_kind = PMU_OHARE_BASED; 31662306a36Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "paddington")) 31762306a36Sopenharmony_ci pmu_kind = PMU_PADDINGTON_BASED; 31862306a36Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "heathrow")) 31962306a36Sopenharmony_ci pmu_kind = PMU_HEATHROW_BASED; 32062306a36Sopenharmony_ci else if (of_device_is_compatible(vias->parent, "Keylargo") 32162306a36Sopenharmony_ci || of_device_is_compatible(vias->parent, "K2-Keylargo")) { 32262306a36Sopenharmony_ci struct device_node *gpiop; 32362306a36Sopenharmony_ci struct device_node *adbp; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci pmu_kind = PMU_KEYLARGO_BASED; 32662306a36Sopenharmony_ci adbp = of_find_node_by_type(NULL, "adb"); 32762306a36Sopenharmony_ci pmu_has_adb = (adbp != NULL); 32862306a36Sopenharmony_ci of_node_put(adbp); 32962306a36Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 33062306a36Sopenharmony_ci PMU_INT_SNDBRT | 33162306a36Sopenharmony_ci PMU_INT_ADB | 33262306a36Sopenharmony_ci PMU_INT_TICK | 33362306a36Sopenharmony_ci PMU_INT_ENVIRONMENT; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci gpiop = of_find_node_by_name(NULL, "gpio"); 33662306a36Sopenharmony_ci if (gpiop) { 33762306a36Sopenharmony_ci if (!of_address_to_resource(gpiop, 0, &res)) 33862306a36Sopenharmony_ci gpio_reg = ioremap(res.start, 0x10); 33962306a36Sopenharmony_ci of_node_put(gpiop); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci if (gpio_reg == NULL) { 34262306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); 34362306a36Sopenharmony_ci goto fail; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } else 34662306a36Sopenharmony_ci pmu_kind = PMU_UNKNOWN; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci via1 = via2 = ioremap(taddr, 0x2000); 34962306a36Sopenharmony_ci if (via1 == NULL) { 35062306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: Can't map address !\n"); 35162306a36Sopenharmony_ci goto fail_via_remap; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci out_8(&via1[IER], IER_CLR | 0x7f); /* disable all intrs */ 35562306a36Sopenharmony_ci out_8(&via1[IFR], 0x7f); /* clear IFR */ 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci pmu_state = idle; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!init_pmu()) 36062306a36Sopenharmony_ci goto fail_init; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci sys_ctrler = SYS_CTRLER_PMU; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 1; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci fail_init: 36762306a36Sopenharmony_ci iounmap(via1); 36862306a36Sopenharmony_ci via1 = via2 = NULL; 36962306a36Sopenharmony_ci fail_via_remap: 37062306a36Sopenharmony_ci iounmap(gpio_reg); 37162306a36Sopenharmony_ci gpio_reg = NULL; 37262306a36Sopenharmony_ci fail: 37362306a36Sopenharmony_ci of_node_put(vias); 37462306a36Sopenharmony_ci vias = NULL; 37562306a36Sopenharmony_ci pmu_state = uninitialized; 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci#else 37862306a36Sopenharmony_ci if (macintosh_config->adb_type != MAC_ADB_PB2) 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci pmu_kind = PMU_UNKNOWN; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pmu_has_adb = 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci pmu_intr_mask = PMU_INT_PCEJECT | 38662306a36Sopenharmony_ci PMU_INT_SNDBRT | 38762306a36Sopenharmony_ci PMU_INT_ADB | 38862306a36Sopenharmony_ci PMU_INT_TICK; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pmu_state = idle; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!init_pmu()) { 39362306a36Sopenharmony_ci pmu_state = uninitialized; 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 1; 39862306a36Sopenharmony_ci#endif /* !CONFIG_PPC_PMAC */ 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci#ifdef CONFIG_ADB 40262306a36Sopenharmony_cistatic int pmu_probe(void) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci return pmu_state == uninitialized ? -ENODEV : 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int pmu_init(void) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci return pmu_state == uninitialized ? -ENODEV : 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci#endif /* CONFIG_ADB */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* 41462306a36Sopenharmony_ci * We can't wait until pmu_init gets called, that happens too late. 41562306a36Sopenharmony_ci * It happens after IDE and SCSI initialization, which can take a few 41662306a36Sopenharmony_ci * seconds, and by that time the PMU could have given up on us and 41762306a36Sopenharmony_ci * turned us off. 41862306a36Sopenharmony_ci * Thus this is called with arch_initcall rather than device_initcall. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_cistatic int __init via_pmu_start(void) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci unsigned int __maybe_unused irq; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (pmu_state == uninitialized) 42562306a36Sopenharmony_ci return -ENODEV; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci batt_req.complete = 1; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 43062306a36Sopenharmony_ci irq = irq_of_parse_and_map(vias, 0); 43162306a36Sopenharmony_ci if (!irq) { 43262306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: can't map interrupt\n"); 43362306a36Sopenharmony_ci return -ENODEV; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci /* We set IRQF_NO_SUSPEND because we don't want the interrupt 43662306a36Sopenharmony_ci * to be disabled between the 2 passes of driver suspend, we 43762306a36Sopenharmony_ci * control our own disabling for that one 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci if (request_irq(irq, via_pmu_interrupt, IRQF_NO_SUSPEND, 44062306a36Sopenharmony_ci "VIA-PMU", (void *)0)) { 44162306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: can't request irq %d\n", irq); 44262306a36Sopenharmony_ci return -ENODEV; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 44662306a36Sopenharmony_ci gpio_node = of_find_node_by_name(NULL, "extint-gpio1"); 44762306a36Sopenharmony_ci if (gpio_node == NULL) 44862306a36Sopenharmony_ci gpio_node = of_find_node_by_name(NULL, 44962306a36Sopenharmony_ci "pmu-interrupt"); 45062306a36Sopenharmony_ci if (gpio_node) 45162306a36Sopenharmony_ci gpio_irq = irq_of_parse_and_map(gpio_node, 0); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (gpio_irq) { 45462306a36Sopenharmony_ci if (request_irq(gpio_irq, gpio1_interrupt, 45562306a36Sopenharmony_ci IRQF_NO_SUSPEND, "GPIO1 ADB", 45662306a36Sopenharmony_ci (void *)0)) 45762306a36Sopenharmony_ci printk(KERN_ERR "pmu: can't get irq %d" 45862306a36Sopenharmony_ci " (GPIO1)\n", gpio_irq); 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci gpio_irq_enabled = 1; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Enable interrupts */ 46562306a36Sopenharmony_ci out_8(&via1[IER], IER_SET | SR_INT | CB1_INT); 46662306a36Sopenharmony_ci#else 46762306a36Sopenharmony_ci if (request_irq(IRQ_MAC_ADB_SR, via_pmu_interrupt, IRQF_NO_SUSPEND, 46862306a36Sopenharmony_ci "VIA-PMU-SR", NULL)) { 46962306a36Sopenharmony_ci pr_err("%s: couldn't get SR irq\n", __func__); 47062306a36Sopenharmony_ci return -ENODEV; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci if (request_irq(IRQ_MAC_ADB_CL, via_pmu_interrupt, IRQF_NO_SUSPEND, 47362306a36Sopenharmony_ci "VIA-PMU-CL", NULL)) { 47462306a36Sopenharmony_ci pr_err("%s: couldn't get CL irq\n", __func__); 47562306a36Sopenharmony_ci free_irq(IRQ_MAC_ADB_SR, NULL); 47662306a36Sopenharmony_ci return -ENODEV; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci#endif /* !CONFIG_PPC_PMAC */ 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci pmu_fully_inited = 1; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Make sure PMU settle down before continuing. This is _very_ important 48362306a36Sopenharmony_ci * since the IDE probe may shut interrupts down for quite a bit of time. If 48462306a36Sopenharmony_ci * a PMU communication is pending while this happens, the PMU may timeout 48562306a36Sopenharmony_ci * Not that on Core99 machines, the PMU keeps sending us environement 48662306a36Sopenharmony_ci * messages, we should find a way to either fix IDE or make it call 48762306a36Sopenharmony_ci * pmu_suspend() before masking interrupts. This can also happens while 48862306a36Sopenharmony_ci * scolling with some fbdevs. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_ci do { 49162306a36Sopenharmony_ci pmu_poll(); 49262306a36Sopenharmony_ci } while (pmu_state != idle); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciarch_initcall(via_pmu_start); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * This has to be done after pci_init, which is a subsys_initcall. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_cistatic int __init via_pmu_dev_init(void) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci if (pmu_state == uninitialized) 50562306a36Sopenharmony_ci return -ENODEV; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 50862306a36Sopenharmony_ci /* Initialize backlight */ 50962306a36Sopenharmony_ci pmu_backlight_init(); 51062306a36Sopenharmony_ci#endif 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#ifdef CONFIG_PPC32 51362306a36Sopenharmony_ci if (of_machine_is_compatible("AAPL,3400/2400") || 51462306a36Sopenharmony_ci of_machine_is_compatible("AAPL,3500")) { 51562306a36Sopenharmony_ci int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, 51662306a36Sopenharmony_ci NULL, PMAC_MB_INFO_MODEL, 0); 51762306a36Sopenharmony_ci pmu_battery_count = 1; 51862306a36Sopenharmony_ci if (mb == PMAC_TYPE_COMET) 51962306a36Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET; 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER; 52262306a36Sopenharmony_ci } else if (of_machine_is_compatible("AAPL,PowerBook1998") || 52362306a36Sopenharmony_ci of_machine_is_compatible("PowerBook1,1")) { 52462306a36Sopenharmony_ci pmu_battery_count = 2; 52562306a36Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; 52662306a36Sopenharmony_ci pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; 52762306a36Sopenharmony_ci } else { 52862306a36Sopenharmony_ci struct device_node* prim = 52962306a36Sopenharmony_ci of_find_node_by_name(NULL, "power-mgt"); 53062306a36Sopenharmony_ci const u32 *prim_info = NULL; 53162306a36Sopenharmony_ci if (prim) 53262306a36Sopenharmony_ci prim_info = of_get_property(prim, "prim-info", NULL); 53362306a36Sopenharmony_ci if (prim_info) { 53462306a36Sopenharmony_ci /* Other stuffs here yet unknown */ 53562306a36Sopenharmony_ci pmu_battery_count = (prim_info[6] >> 16) & 0xff; 53662306a36Sopenharmony_ci pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; 53762306a36Sopenharmony_ci if (pmu_battery_count > 1) 53862306a36Sopenharmony_ci pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci of_node_put(prim); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci#endif /* CONFIG_PPC32 */ 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Create /proc/pmu */ 54562306a36Sopenharmony_ci proc_pmu_root = proc_mkdir("pmu", NULL); 54662306a36Sopenharmony_ci if (proc_pmu_root) { 54762306a36Sopenharmony_ci long i; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci for (i=0; i<pmu_battery_count; i++) { 55062306a36Sopenharmony_ci char title[16]; 55162306a36Sopenharmony_ci sprintf(title, "battery_%ld", i); 55262306a36Sopenharmony_ci proc_pmu_batt[i] = proc_create_single_data(title, 0, 55362306a36Sopenharmony_ci proc_pmu_root, pmu_battery_proc_show, 55462306a36Sopenharmony_ci (void *)i); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci proc_pmu_info = proc_create_single("info", 0, proc_pmu_root, 55862306a36Sopenharmony_ci pmu_info_proc_show); 55962306a36Sopenharmony_ci proc_pmu_irqstats = proc_create_single("interrupts", 0, 56062306a36Sopenharmony_ci proc_pmu_root, pmu_irqstats_proc_show); 56162306a36Sopenharmony_ci proc_pmu_options = proc_create("options", 0600, proc_pmu_root, 56262306a36Sopenharmony_ci &pmu_options_proc_ops); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cidevice_initcall(via_pmu_dev_init); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int 57062306a36Sopenharmony_ciinit_pmu(void) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int timeout; 57362306a36Sopenharmony_ci struct adb_request req; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Negate TREQ. Set TACK to input and TREQ to output. */ 57662306a36Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) | TREQ); 57762306a36Sopenharmony_ci out_8(&via2[DIRB], (in_8(&via2[DIRB]) | TREQ) & ~TACK); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 58062306a36Sopenharmony_ci timeout = 100000; 58162306a36Sopenharmony_ci while (!req.complete) { 58262306a36Sopenharmony_ci if (--timeout < 0) { 58362306a36Sopenharmony_ci printk(KERN_ERR "init_pmu: no response from PMU\n"); 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci udelay(10); 58762306a36Sopenharmony_ci pmu_poll(); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* ack all pending interrupts */ 59162306a36Sopenharmony_ci timeout = 100000; 59262306a36Sopenharmony_ci interrupt_data[0][0] = 1; 59362306a36Sopenharmony_ci while (interrupt_data[0][0] || pmu_state != idle) { 59462306a36Sopenharmony_ci if (--timeout < 0) { 59562306a36Sopenharmony_ci printk(KERN_ERR "init_pmu: timed out acking intrs\n"); 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci if (pmu_state == idle) 59962306a36Sopenharmony_ci adb_int_pending = 1; 60062306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 60162306a36Sopenharmony_ci udelay(10); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Tell PMU we are ready. */ 60562306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 60662306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 60762306a36Sopenharmony_ci while (!req.complete) 60862306a36Sopenharmony_ci pmu_poll(); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Read PMU version */ 61262306a36Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_GET_VERSION); 61362306a36Sopenharmony_ci pmu_wait_complete(&req); 61462306a36Sopenharmony_ci if (req.reply_len > 0) 61562306a36Sopenharmony_ci pmu_version = req.reply[0]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* Read server mode setting */ 61862306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) { 61962306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, 62062306a36Sopenharmony_ci PMU_PWR_GET_POWERUP_EVENTS); 62162306a36Sopenharmony_ci pmu_wait_complete(&req); 62262306a36Sopenharmony_ci if (req.reply_len == 2) { 62362306a36Sopenharmony_ci if (req.reply[1] & PMU_PWR_WAKEUP_AC_INSERT) 62462306a36Sopenharmony_ci option_server_mode = 1; 62562306a36Sopenharmony_ci printk(KERN_INFO "via-pmu: Server Mode is %s\n", 62662306a36Sopenharmony_ci option_server_mode ? "enabled" : "disabled"); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci printk(KERN_INFO "PMU driver v%d initialized for %s, firmware: %02x\n", 63162306a36Sopenharmony_ci PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return 1; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciint 63762306a36Sopenharmony_cipmu_get_model(void) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci return pmu_kind; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void pmu_set_server_mode(int server_mode) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct adb_request req; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci option_server_mode = server_mode; 65062306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, PMU_PWR_GET_POWERUP_EVENTS); 65162306a36Sopenharmony_ci pmu_wait_complete(&req); 65262306a36Sopenharmony_ci if (req.reply_len < 2) 65362306a36Sopenharmony_ci return; 65462306a36Sopenharmony_ci if (server_mode) 65562306a36Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, 65662306a36Sopenharmony_ci PMU_PWR_SET_POWERUP_EVENTS, 65762306a36Sopenharmony_ci req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 65862306a36Sopenharmony_ci else 65962306a36Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, 66062306a36Sopenharmony_ci PMU_PWR_CLR_POWERUP_EVENTS, 66162306a36Sopenharmony_ci req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 66262306a36Sopenharmony_ci pmu_wait_complete(&req); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/* This new version of the code for 2400/3400/3500 powerbooks 66662306a36Sopenharmony_ci * is inspired from the implementation in gkrellm-pmu 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_cistatic void 66962306a36Sopenharmony_cidone_battery_state_ohare(struct adb_request* req) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 67262306a36Sopenharmony_ci /* format: 67362306a36Sopenharmony_ci * [0] : flags 67462306a36Sopenharmony_ci * 0x01 : AC indicator 67562306a36Sopenharmony_ci * 0x02 : charging 67662306a36Sopenharmony_ci * 0x04 : battery exist 67762306a36Sopenharmony_ci * 0x08 : 67862306a36Sopenharmony_ci * 0x10 : 67962306a36Sopenharmony_ci * 0x20 : full charged 68062306a36Sopenharmony_ci * 0x40 : pcharge reset 68162306a36Sopenharmony_ci * 0x80 : battery exist 68262306a36Sopenharmony_ci * 68362306a36Sopenharmony_ci * [1][2] : battery voltage 68462306a36Sopenharmony_ci * [3] : CPU temperature 68562306a36Sopenharmony_ci * [4] : battery temperature 68662306a36Sopenharmony_ci * [5] : current 68762306a36Sopenharmony_ci * [6][7] : pcharge 68862306a36Sopenharmony_ci * --tkoba 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci unsigned int bat_flags = PMU_BATT_TYPE_HOOPER; 69162306a36Sopenharmony_ci long pcharge, charge, vb, vmax, lmax; 69262306a36Sopenharmony_ci long vmax_charging, vmax_charged; 69362306a36Sopenharmony_ci long amperage, voltage, time, max; 69462306a36Sopenharmony_ci int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, 69562306a36Sopenharmony_ci NULL, PMAC_MB_INFO_MODEL, 0); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (req->reply[0] & 0x01) 69862306a36Sopenharmony_ci pmu_power_flags |= PMU_PWR_AC_PRESENT; 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci pmu_power_flags &= ~PMU_PWR_AC_PRESENT; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (mb == PMAC_TYPE_COMET) { 70362306a36Sopenharmony_ci vmax_charged = 189; 70462306a36Sopenharmony_ci vmax_charging = 213; 70562306a36Sopenharmony_ci lmax = 6500; 70662306a36Sopenharmony_ci } else { 70762306a36Sopenharmony_ci vmax_charged = 330; 70862306a36Sopenharmony_ci vmax_charging = 330; 70962306a36Sopenharmony_ci lmax = 6500; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci vmax = vmax_charged; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* If battery installed */ 71462306a36Sopenharmony_ci if (req->reply[0] & 0x04) { 71562306a36Sopenharmony_ci bat_flags |= PMU_BATT_PRESENT; 71662306a36Sopenharmony_ci if (req->reply[0] & 0x02) 71762306a36Sopenharmony_ci bat_flags |= PMU_BATT_CHARGING; 71862306a36Sopenharmony_ci vb = (req->reply[1] << 8) | req->reply[2]; 71962306a36Sopenharmony_ci voltage = (vb * 265 + 72665) / 10; 72062306a36Sopenharmony_ci amperage = req->reply[5]; 72162306a36Sopenharmony_ci if ((req->reply[0] & 0x01) == 0) { 72262306a36Sopenharmony_ci if (amperage > 200) 72362306a36Sopenharmony_ci vb += ((amperage - 200) * 15)/100; 72462306a36Sopenharmony_ci } else if (req->reply[0] & 0x02) { 72562306a36Sopenharmony_ci vb = (vb * 97) / 100; 72662306a36Sopenharmony_ci vmax = vmax_charging; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci charge = (100 * vb) / vmax; 72962306a36Sopenharmony_ci if (req->reply[0] & 0x40) { 73062306a36Sopenharmony_ci pcharge = (req->reply[6] << 8) + req->reply[7]; 73162306a36Sopenharmony_ci if (pcharge > lmax) 73262306a36Sopenharmony_ci pcharge = lmax; 73362306a36Sopenharmony_ci pcharge *= 100; 73462306a36Sopenharmony_ci pcharge = 100 - pcharge / lmax; 73562306a36Sopenharmony_ci if (pcharge < charge) 73662306a36Sopenharmony_ci charge = pcharge; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci if (amperage > 0) 73962306a36Sopenharmony_ci time = (charge * 16440) / amperage; 74062306a36Sopenharmony_ci else 74162306a36Sopenharmony_ci time = 0; 74262306a36Sopenharmony_ci max = 100; 74362306a36Sopenharmony_ci amperage = -amperage; 74462306a36Sopenharmony_ci } else 74562306a36Sopenharmony_ci charge = max = amperage = voltage = time = 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].flags = bat_flags; 74862306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].charge = charge; 74962306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].max_charge = max; 75062306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].amperage = amperage; 75162306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].voltage = voltage; 75262306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining = time; 75362306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci clear_bit(0, &async_req_locks); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void 75962306a36Sopenharmony_cidone_battery_state_smart(struct adb_request* req) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci /* format: 76262306a36Sopenharmony_ci * [0] : format of this structure (known: 3,4,5) 76362306a36Sopenharmony_ci * [1] : flags 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * format 3 & 4: 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * [2] : charge 76862306a36Sopenharmony_ci * [3] : max charge 76962306a36Sopenharmony_ci * [4] : current 77062306a36Sopenharmony_ci * [5] : voltage 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * format 5: 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * [2][3] : charge 77562306a36Sopenharmony_ci * [4][5] : max charge 77662306a36Sopenharmony_ci * [6][7] : current 77762306a36Sopenharmony_ci * [8][9] : voltage 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci unsigned int bat_flags = PMU_BATT_TYPE_SMART; 78162306a36Sopenharmony_ci int amperage; 78262306a36Sopenharmony_ci unsigned int capa, max, voltage; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (req->reply[1] & 0x01) 78562306a36Sopenharmony_ci pmu_power_flags |= PMU_PWR_AC_PRESENT; 78662306a36Sopenharmony_ci else 78762306a36Sopenharmony_ci pmu_power_flags &= ~PMU_PWR_AC_PRESENT; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci capa = max = amperage = voltage = 0; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (req->reply[1] & 0x04) { 79362306a36Sopenharmony_ci bat_flags |= PMU_BATT_PRESENT; 79462306a36Sopenharmony_ci switch(req->reply[0]) { 79562306a36Sopenharmony_ci case 3: 79662306a36Sopenharmony_ci case 4: capa = req->reply[2]; 79762306a36Sopenharmony_ci max = req->reply[3]; 79862306a36Sopenharmony_ci amperage = *((signed char *)&req->reply[4]); 79962306a36Sopenharmony_ci voltage = req->reply[5]; 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case 5: capa = (req->reply[2] << 8) | req->reply[3]; 80262306a36Sopenharmony_ci max = (req->reply[4] << 8) | req->reply[5]; 80362306a36Sopenharmony_ci amperage = *((signed short *)&req->reply[6]); 80462306a36Sopenharmony_ci voltage = (req->reply[8] << 8) | req->reply[9]; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci default: 80762306a36Sopenharmony_ci pr_warn("pmu.c: unrecognized battery info, " 80862306a36Sopenharmony_ci "len: %d, %4ph\n", req->reply_len, 80962306a36Sopenharmony_ci req->reply); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if ((req->reply[1] & 0x01) && (amperage > 0)) 81562306a36Sopenharmony_ci bat_flags |= PMU_BATT_CHARGING; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].flags = bat_flags; 81862306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].charge = capa; 81962306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].max_charge = max; 82062306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].amperage = amperage; 82162306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].voltage = voltage; 82262306a36Sopenharmony_ci if (amperage) { 82362306a36Sopenharmony_ci if ((req->reply[1] & 0x01) && (amperage > 0)) 82462306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining 82562306a36Sopenharmony_ci = ((max-capa) * 3600) / amperage; 82662306a36Sopenharmony_ci else 82762306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining 82862306a36Sopenharmony_ci = (capa * 3600) / (-amperage); 82962306a36Sopenharmony_ci } else 83062306a36Sopenharmony_ci pmu_batteries[pmu_cur_battery].time_remaining = 0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci clear_bit(0, &async_req_locks); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic void 83862306a36Sopenharmony_ciquery_battery_state(void) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci if (test_and_set_bit(0, &async_req_locks)) 84162306a36Sopenharmony_ci return; 84262306a36Sopenharmony_ci if (pmu_kind == PMU_OHARE_BASED) 84362306a36Sopenharmony_ci pmu_request(&batt_req, done_battery_state_ohare, 84462306a36Sopenharmony_ci 1, PMU_BATTERY_STATE); 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci pmu_request(&batt_req, done_battery_state_smart, 84762306a36Sopenharmony_ci 2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 85162306a36Sopenharmony_cistatic int pmu_info_proc_show(struct seq_file *m, void *v) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci seq_printf(m, "PMU driver version : %d\n", PMU_DRIVER_VERSION); 85462306a36Sopenharmony_ci seq_printf(m, "PMU firmware version : %02x\n", pmu_version); 85562306a36Sopenharmony_ci seq_printf(m, "AC Power : %d\n", 85662306a36Sopenharmony_ci ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0) || pmu_battery_count == 0); 85762306a36Sopenharmony_ci seq_printf(m, "Battery count : %d\n", pmu_battery_count); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic int pmu_irqstats_proc_show(struct seq_file *m, void *v) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci int i; 86562306a36Sopenharmony_ci static const char *irq_names[NUM_IRQ_STATS] = { 86662306a36Sopenharmony_ci "Unknown interrupt (type 0)", 86762306a36Sopenharmony_ci "Unknown interrupt (type 1)", 86862306a36Sopenharmony_ci "PC-Card eject button", 86962306a36Sopenharmony_ci "Sound/Brightness button", 87062306a36Sopenharmony_ci "ADB message", 87162306a36Sopenharmony_ci "Battery state change", 87262306a36Sopenharmony_ci "Environment interrupt", 87362306a36Sopenharmony_ci "Tick timer", 87462306a36Sopenharmony_ci "Ghost interrupt (zero len)", 87562306a36Sopenharmony_ci "Empty interrupt (empty mask)", 87662306a36Sopenharmony_ci "Max irqs in a row", 87762306a36Sopenharmony_ci "Total CB1 triggered events", 87862306a36Sopenharmony_ci "Total GPIO1 triggered events", 87962306a36Sopenharmony_ci }; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci for (i = 0; i < NUM_IRQ_STATS; i++) { 88262306a36Sopenharmony_ci seq_printf(m, " %2u: %10u (%s)\n", 88362306a36Sopenharmony_ci i, pmu_irq_stats[i], irq_names[i]); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int pmu_battery_proc_show(struct seq_file *m, void *v) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci long batnum = (long)m->private; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci seq_putc(m, '\n'); 89362306a36Sopenharmony_ci seq_printf(m, "flags : %08x\n", pmu_batteries[batnum].flags); 89462306a36Sopenharmony_ci seq_printf(m, "charge : %d\n", pmu_batteries[batnum].charge); 89562306a36Sopenharmony_ci seq_printf(m, "max_charge : %d\n", pmu_batteries[batnum].max_charge); 89662306a36Sopenharmony_ci seq_printf(m, "current : %d\n", pmu_batteries[batnum].amperage); 89762306a36Sopenharmony_ci seq_printf(m, "voltage : %d\n", pmu_batteries[batnum].voltage); 89862306a36Sopenharmony_ci seq_printf(m, "time rem. : %d\n", pmu_batteries[batnum].time_remaining); 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int pmu_options_proc_show(struct seq_file *m, void *v) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 90562306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && 90662306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) 90762306a36Sopenharmony_ci seq_printf(m, "lid_wakeup=%d\n", option_lid_wakeup); 90862306a36Sopenharmony_ci#endif 90962306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) 91062306a36Sopenharmony_ci seq_printf(m, "server_mode=%d\n", option_server_mode); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic int pmu_options_proc_open(struct inode *inode, struct file *file) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci return single_open(file, pmu_options_proc_show, NULL); 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic ssize_t pmu_options_proc_write(struct file *file, 92162306a36Sopenharmony_ci const char __user *buffer, size_t count, loff_t *pos) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci char tmp[33]; 92462306a36Sopenharmony_ci char *label, *val; 92562306a36Sopenharmony_ci size_t fcount = count; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!count) 92862306a36Sopenharmony_ci return -EINVAL; 92962306a36Sopenharmony_ci if (count > 32) 93062306a36Sopenharmony_ci count = 32; 93162306a36Sopenharmony_ci if (copy_from_user(tmp, buffer, count)) 93262306a36Sopenharmony_ci return -EFAULT; 93362306a36Sopenharmony_ci tmp[count] = 0; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci label = tmp; 93662306a36Sopenharmony_ci while(*label == ' ') 93762306a36Sopenharmony_ci label++; 93862306a36Sopenharmony_ci val = label; 93962306a36Sopenharmony_ci while(*val && (*val != '=')) { 94062306a36Sopenharmony_ci if (*val == ' ') 94162306a36Sopenharmony_ci *val = 0; 94262306a36Sopenharmony_ci val++; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci if ((*val) == 0) 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci *(val++) = 0; 94762306a36Sopenharmony_ci while(*val == ' ') 94862306a36Sopenharmony_ci val++; 94962306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 95062306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && 95162306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) 95262306a36Sopenharmony_ci if (!strcmp(label, "lid_wakeup")) 95362306a36Sopenharmony_ci option_lid_wakeup = ((*val) == '1'); 95462306a36Sopenharmony_ci#endif 95562306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) { 95662306a36Sopenharmony_ci int new_value; 95762306a36Sopenharmony_ci new_value = ((*val) == '1'); 95862306a36Sopenharmony_ci if (new_value != option_server_mode) 95962306a36Sopenharmony_ci pmu_set_server_mode(new_value); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci return fcount; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic const struct proc_ops pmu_options_proc_ops = { 96562306a36Sopenharmony_ci .proc_open = pmu_options_proc_open, 96662306a36Sopenharmony_ci .proc_read = seq_read, 96762306a36Sopenharmony_ci .proc_lseek = seq_lseek, 96862306a36Sopenharmony_ci .proc_release = single_release, 96962306a36Sopenharmony_ci .proc_write = pmu_options_proc_write, 97062306a36Sopenharmony_ci}; 97162306a36Sopenharmony_ci#endif 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci#ifdef CONFIG_ADB 97462306a36Sopenharmony_ci/* Send an ADB command */ 97562306a36Sopenharmony_cistatic int pmu_send_request(struct adb_request *req, int sync) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci int i, ret; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited) { 98062306a36Sopenharmony_ci req->complete = 1; 98162306a36Sopenharmony_ci return -ENXIO; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci ret = -EINVAL; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci switch (req->data[0]) { 98762306a36Sopenharmony_ci case PMU_PACKET: 98862306a36Sopenharmony_ci for (i = 0; i < req->nbytes - 1; ++i) 98962306a36Sopenharmony_ci req->data[i] = req->data[i+1]; 99062306a36Sopenharmony_ci --req->nbytes; 99162306a36Sopenharmony_ci if (pmu_data_len[req->data[0]][1] != 0) { 99262306a36Sopenharmony_ci req->reply[0] = ADB_RET_OK; 99362306a36Sopenharmony_ci req->reply_len = 1; 99462306a36Sopenharmony_ci } else 99562306a36Sopenharmony_ci req->reply_len = 0; 99662306a36Sopenharmony_ci ret = pmu_queue_request(req); 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci case CUDA_PACKET: 99962306a36Sopenharmony_ci switch (req->data[1]) { 100062306a36Sopenharmony_ci case CUDA_GET_TIME: 100162306a36Sopenharmony_ci if (req->nbytes != 2) 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci req->data[0] = PMU_READ_RTC; 100462306a36Sopenharmony_ci req->nbytes = 1; 100562306a36Sopenharmony_ci req->reply_len = 3; 100662306a36Sopenharmony_ci req->reply[0] = CUDA_PACKET; 100762306a36Sopenharmony_ci req->reply[1] = 0; 100862306a36Sopenharmony_ci req->reply[2] = CUDA_GET_TIME; 100962306a36Sopenharmony_ci ret = pmu_queue_request(req); 101062306a36Sopenharmony_ci break; 101162306a36Sopenharmony_ci case CUDA_SET_TIME: 101262306a36Sopenharmony_ci if (req->nbytes != 6) 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci req->data[0] = PMU_SET_RTC; 101562306a36Sopenharmony_ci req->nbytes = 5; 101662306a36Sopenharmony_ci for (i = 1; i <= 4; ++i) 101762306a36Sopenharmony_ci req->data[i] = req->data[i+1]; 101862306a36Sopenharmony_ci req->reply_len = 3; 101962306a36Sopenharmony_ci req->reply[0] = CUDA_PACKET; 102062306a36Sopenharmony_ci req->reply[1] = 0; 102162306a36Sopenharmony_ci req->reply[2] = CUDA_SET_TIME; 102262306a36Sopenharmony_ci ret = pmu_queue_request(req); 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case ADB_PACKET: 102762306a36Sopenharmony_ci if (!pmu_has_adb) 102862306a36Sopenharmony_ci return -ENXIO; 102962306a36Sopenharmony_ci for (i = req->nbytes - 1; i > 1; --i) 103062306a36Sopenharmony_ci req->data[i+2] = req->data[i]; 103162306a36Sopenharmony_ci req->data[3] = req->nbytes - 2; 103262306a36Sopenharmony_ci req->data[2] = pmu_adb_flags; 103362306a36Sopenharmony_ci /*req->data[1] = req->data[1];*/ 103462306a36Sopenharmony_ci req->data[0] = PMU_ADB_CMD; 103562306a36Sopenharmony_ci req->nbytes += 2; 103662306a36Sopenharmony_ci req->reply_expected = 1; 103762306a36Sopenharmony_ci req->reply_len = 0; 103862306a36Sopenharmony_ci ret = pmu_queue_request(req); 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci if (ret) { 104262306a36Sopenharmony_ci req->complete = 1; 104362306a36Sopenharmony_ci return ret; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (sync) 104762306a36Sopenharmony_ci while (!req->complete) 104862306a36Sopenharmony_ci pmu_poll(); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* Enable/disable autopolling */ 105462306a36Sopenharmony_cistatic int __pmu_adb_autopoll(int devs) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct adb_request req; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (devs) { 105962306a36Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, 106062306a36Sopenharmony_ci adb_dev_map >> 8, adb_dev_map); 106162306a36Sopenharmony_ci pmu_adb_flags = 2; 106262306a36Sopenharmony_ci } else { 106362306a36Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF); 106462306a36Sopenharmony_ci pmu_adb_flags = 0; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci while (!req.complete) 106762306a36Sopenharmony_ci pmu_poll(); 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic int pmu_adb_autopoll(int devs) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited || !pmu_has_adb) 107462306a36Sopenharmony_ci return -ENXIO; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci adb_dev_map = devs; 107762306a36Sopenharmony_ci return __pmu_adb_autopoll(devs); 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci/* Reset the ADB bus */ 108162306a36Sopenharmony_cistatic int pmu_adb_reset_bus(void) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct adb_request req; 108462306a36Sopenharmony_ci int save_autopoll = adb_dev_map; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (pmu_state == uninitialized || !pmu_fully_inited || !pmu_has_adb) 108762306a36Sopenharmony_ci return -ENXIO; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* anyone got a better idea?? */ 109062306a36Sopenharmony_ci __pmu_adb_autopoll(0); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci req.nbytes = 4; 109362306a36Sopenharmony_ci req.done = NULL; 109462306a36Sopenharmony_ci req.data[0] = PMU_ADB_CMD; 109562306a36Sopenharmony_ci req.data[1] = ADB_BUSRESET; 109662306a36Sopenharmony_ci req.data[2] = 0; 109762306a36Sopenharmony_ci req.data[3] = 0; 109862306a36Sopenharmony_ci req.data[4] = 0; 109962306a36Sopenharmony_ci req.reply_len = 0; 110062306a36Sopenharmony_ci req.reply_expected = 1; 110162306a36Sopenharmony_ci if (pmu_queue_request(&req) != 0) { 110262306a36Sopenharmony_ci printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n"); 110362306a36Sopenharmony_ci return -EIO; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci pmu_wait_complete(&req); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (save_autopoll != 0) 110862306a36Sopenharmony_ci __pmu_adb_autopoll(save_autopoll); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci#endif /* CONFIG_ADB */ 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci/* Construct and send a pmu request */ 111562306a36Sopenharmony_ciint 111662306a36Sopenharmony_cipmu_request(struct adb_request *req, void (*done)(struct adb_request *), 111762306a36Sopenharmony_ci int nbytes, ...) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci va_list list; 112062306a36Sopenharmony_ci int i; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (pmu_state == uninitialized) 112362306a36Sopenharmony_ci return -ENXIO; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (nbytes < 0 || nbytes > 32) { 112662306a36Sopenharmony_ci printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes); 112762306a36Sopenharmony_ci req->complete = 1; 112862306a36Sopenharmony_ci return -EINVAL; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci req->nbytes = nbytes; 113162306a36Sopenharmony_ci req->done = done; 113262306a36Sopenharmony_ci va_start(list, nbytes); 113362306a36Sopenharmony_ci for (i = 0; i < nbytes; ++i) 113462306a36Sopenharmony_ci req->data[i] = va_arg(list, int); 113562306a36Sopenharmony_ci va_end(list); 113662306a36Sopenharmony_ci req->reply_len = 0; 113762306a36Sopenharmony_ci req->reply_expected = 0; 113862306a36Sopenharmony_ci return pmu_queue_request(req); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ciint 114262306a36Sopenharmony_cipmu_queue_request(struct adb_request *req) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci unsigned long flags; 114562306a36Sopenharmony_ci int nsend; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (pmu_state == uninitialized) { 114862306a36Sopenharmony_ci req->complete = 1; 114962306a36Sopenharmony_ci return -ENXIO; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci if (req->nbytes <= 0) { 115262306a36Sopenharmony_ci req->complete = 1; 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci nsend = pmu_data_len[req->data[0]][0]; 115662306a36Sopenharmony_ci if (nsend >= 0 && req->nbytes != nsend + 1) { 115762306a36Sopenharmony_ci req->complete = 1; 115862306a36Sopenharmony_ci return -EINVAL; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci req->next = NULL; 116262306a36Sopenharmony_ci req->sent = 0; 116362306a36Sopenharmony_ci req->complete = 0; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 116662306a36Sopenharmony_ci if (current_req) { 116762306a36Sopenharmony_ci last_req->next = req; 116862306a36Sopenharmony_ci last_req = req; 116962306a36Sopenharmony_ci } else { 117062306a36Sopenharmony_ci current_req = req; 117162306a36Sopenharmony_ci last_req = req; 117262306a36Sopenharmony_ci if (pmu_state == idle) 117362306a36Sopenharmony_ci pmu_start(); 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci return 0; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic inline void 118162306a36Sopenharmony_ciwait_for_ack(void) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci /* Sightly increased the delay, I had one occurrence of the message 118462306a36Sopenharmony_ci * reported 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci int timeout = 4000; 118762306a36Sopenharmony_ci while ((in_8(&via2[B]) & TACK) == 0) { 118862306a36Sopenharmony_ci if (--timeout < 0) { 118962306a36Sopenharmony_ci printk(KERN_ERR "PMU not responding (!ack)\n"); 119062306a36Sopenharmony_ci return; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci udelay(10); 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/* New PMU seems to be very sensitive to those timings, so we make sure 119762306a36Sopenharmony_ci * PCI is flushed immediately */ 119862306a36Sopenharmony_cistatic inline void 119962306a36Sopenharmony_cisend_byte(int x) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci out_8(&via1[ACR], in_8(&via1[ACR]) | SR_OUT | SR_EXT); 120262306a36Sopenharmony_ci out_8(&via1[SR], x); 120362306a36Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) & ~TREQ); /* assert TREQ */ 120462306a36Sopenharmony_ci (void)in_8(&via2[B]); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic inline void 120862306a36Sopenharmony_cirecv_byte(void) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci out_8(&via1[ACR], (in_8(&via1[ACR]) & ~SR_OUT) | SR_EXT); 121162306a36Sopenharmony_ci in_8(&via1[SR]); /* resets SR */ 121262306a36Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) & ~TREQ); 121362306a36Sopenharmony_ci (void)in_8(&via2[B]); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic inline void 121762306a36Sopenharmony_cipmu_done(struct adb_request *req) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci void (*done)(struct adb_request *) = req->done; 122062306a36Sopenharmony_ci mb(); 122162306a36Sopenharmony_ci req->complete = 1; 122262306a36Sopenharmony_ci /* Here, we assume that if the request has a done member, the 122362306a36Sopenharmony_ci * struct request will survive to setting req->complete to 1 122462306a36Sopenharmony_ci */ 122562306a36Sopenharmony_ci if (done) 122662306a36Sopenharmony_ci (*done)(req); 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic void 123062306a36Sopenharmony_cipmu_start(void) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct adb_request *req; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* assert pmu_state == idle */ 123562306a36Sopenharmony_ci /* get the packet to send */ 123662306a36Sopenharmony_ci req = current_req; 123762306a36Sopenharmony_ci if (!req || pmu_state != idle 123862306a36Sopenharmony_ci || (/*req->reply_expected && */req_awaiting_reply)) 123962306a36Sopenharmony_ci return; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci pmu_state = sending; 124262306a36Sopenharmony_ci data_index = 1; 124362306a36Sopenharmony_ci data_len = pmu_data_len[req->data[0]][0]; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* Sounds safer to make sure ACK is high before writing. This helped 124662306a36Sopenharmony_ci * kill a problem with ADB and some iBooks 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_ci wait_for_ack(); 124962306a36Sopenharmony_ci /* set the shift register to shift out and send a byte */ 125062306a36Sopenharmony_ci send_byte(req->data[0]); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_civoid 125462306a36Sopenharmony_cipmu_poll(void) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci if (pmu_state == uninitialized) 125762306a36Sopenharmony_ci return; 125862306a36Sopenharmony_ci if (disable_poll) 125962306a36Sopenharmony_ci return; 126062306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_civoid 126462306a36Sopenharmony_cipmu_poll_adb(void) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci if (pmu_state == uninitialized) 126762306a36Sopenharmony_ci return; 126862306a36Sopenharmony_ci if (disable_poll) 126962306a36Sopenharmony_ci return; 127062306a36Sopenharmony_ci /* Kicks ADB read when PMU is suspended */ 127162306a36Sopenharmony_ci adb_int_pending = 1; 127262306a36Sopenharmony_ci do { 127362306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 127462306a36Sopenharmony_ci } while (pmu_suspended && (adb_int_pending || pmu_state != idle 127562306a36Sopenharmony_ci || req_awaiting_reply)); 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_civoid 127962306a36Sopenharmony_cipmu_wait_complete(struct adb_request *req) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci if (pmu_state == uninitialized) 128262306a36Sopenharmony_ci return; 128362306a36Sopenharmony_ci while((pmu_state != idle && pmu_state != locked) || !req->complete) 128462306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci/* This function loops until the PMU is idle and prevents it from 128862306a36Sopenharmony_ci * anwsering to ADB interrupts. pmu_request can still be called. 128962306a36Sopenharmony_ci * This is done to avoid spurrious shutdowns when we know we'll have 129062306a36Sopenharmony_ci * interrupts switched off for a long time 129162306a36Sopenharmony_ci */ 129262306a36Sopenharmony_civoid 129362306a36Sopenharmony_cipmu_suspend(void) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci unsigned long flags; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (pmu_state == uninitialized) 129862306a36Sopenharmony_ci return; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 130162306a36Sopenharmony_ci pmu_suspended++; 130262306a36Sopenharmony_ci if (pmu_suspended > 1) { 130362306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 130462306a36Sopenharmony_ci return; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci do { 130862306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 130962306a36Sopenharmony_ci if (req_awaiting_reply) 131062306a36Sopenharmony_ci adb_int_pending = 1; 131162306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 131262306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 131362306a36Sopenharmony_ci if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) { 131462306a36Sopenharmony_ci if (gpio_irq >= 0) 131562306a36Sopenharmony_ci disable_irq_nosync(gpio_irq); 131662306a36Sopenharmony_ci out_8(&via1[IER], CB1_INT | IER_CLR); 131762306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci } while (1); 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_civoid 132462306a36Sopenharmony_cipmu_resume(void) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci unsigned long flags; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (pmu_state == uninitialized || pmu_suspended < 1) 132962306a36Sopenharmony_ci return; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 133262306a36Sopenharmony_ci pmu_suspended--; 133362306a36Sopenharmony_ci if (pmu_suspended > 0) { 133462306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 133562306a36Sopenharmony_ci return; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci adb_int_pending = 1; 133862306a36Sopenharmony_ci if (gpio_irq >= 0) 133962306a36Sopenharmony_ci enable_irq(gpio_irq); 134062306a36Sopenharmony_ci out_8(&via1[IER], CB1_INT | IER_SET); 134162306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 134262306a36Sopenharmony_ci pmu_poll(); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci/* Interrupt data could be the result data from an ADB cmd */ 134662306a36Sopenharmony_cistatic void 134762306a36Sopenharmony_cipmu_handle_data(unsigned char *data, int len) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci unsigned char ints; 135062306a36Sopenharmony_ci int idx; 135162306a36Sopenharmony_ci int i = 0; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci asleep = 0; 135462306a36Sopenharmony_ci if (drop_interrupts || len < 1) { 135562306a36Sopenharmony_ci adb_int_pending = 0; 135662306a36Sopenharmony_ci pmu_irq_stats[8]++; 135762306a36Sopenharmony_ci return; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* Get PMU interrupt mask */ 136162306a36Sopenharmony_ci ints = data[0]; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci /* Record zero interrupts for stats */ 136462306a36Sopenharmony_ci if (ints == 0) 136562306a36Sopenharmony_ci pmu_irq_stats[9]++; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* Hack to deal with ADB autopoll flag */ 136862306a36Sopenharmony_ci if (ints & PMU_INT_ADB) 136962306a36Sopenharmony_ci ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cinext: 137262306a36Sopenharmony_ci if (ints == 0) { 137362306a36Sopenharmony_ci if (i > pmu_irq_stats[10]) 137462306a36Sopenharmony_ci pmu_irq_stats[10] = i; 137562306a36Sopenharmony_ci return; 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci i++; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci idx = ffs(ints) - 1; 138062306a36Sopenharmony_ci ints &= ~BIT(idx); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci pmu_irq_stats[idx]++; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* Note: for some reason, we get an interrupt with len=1, 138562306a36Sopenharmony_ci * data[0]==0 after each normal ADB interrupt, at least 138662306a36Sopenharmony_ci * on the Pismo. Still investigating... --BenH 138762306a36Sopenharmony_ci */ 138862306a36Sopenharmony_ci switch (BIT(idx)) { 138962306a36Sopenharmony_ci case PMU_INT_ADB: 139062306a36Sopenharmony_ci if ((data[0] & PMU_INT_ADB_AUTO) == 0) { 139162306a36Sopenharmony_ci struct adb_request *req = req_awaiting_reply; 139262306a36Sopenharmony_ci if (!req) { 139362306a36Sopenharmony_ci printk(KERN_ERR "PMU: extra ADB reply\n"); 139462306a36Sopenharmony_ci return; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci req_awaiting_reply = NULL; 139762306a36Sopenharmony_ci if (len <= 2) 139862306a36Sopenharmony_ci req->reply_len = 0; 139962306a36Sopenharmony_ci else { 140062306a36Sopenharmony_ci memcpy(req->reply, data + 1, len - 1); 140162306a36Sopenharmony_ci req->reply_len = len - 1; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci pmu_done(req); 140462306a36Sopenharmony_ci } else { 140562306a36Sopenharmony_ci#ifdef CONFIG_XMON 140662306a36Sopenharmony_ci if (len == 4 && data[1] == 0x2c) { 140762306a36Sopenharmony_ci extern int xmon_wants_key, xmon_adb_keycode; 140862306a36Sopenharmony_ci if (xmon_wants_key) { 140962306a36Sopenharmony_ci xmon_adb_keycode = data[2]; 141062306a36Sopenharmony_ci return; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci#endif /* CONFIG_XMON */ 141462306a36Sopenharmony_ci#ifdef CONFIG_ADB 141562306a36Sopenharmony_ci /* 141662306a36Sopenharmony_ci * XXX On the [23]400 the PMU gives us an up 141762306a36Sopenharmony_ci * event for keycodes 0x74 or 0x75 when the PC 141862306a36Sopenharmony_ci * card eject buttons are released, so we 141962306a36Sopenharmony_ci * ignore those events. 142062306a36Sopenharmony_ci */ 142162306a36Sopenharmony_ci if (!(pmu_kind == PMU_OHARE_BASED && len == 4 142262306a36Sopenharmony_ci && data[1] == 0x2c && data[3] == 0xff 142362306a36Sopenharmony_ci && (data[2] & ~1) == 0xf4)) 142462306a36Sopenharmony_ci adb_input(data+1, len-1, 1); 142562306a36Sopenharmony_ci#endif /* CONFIG_ADB */ 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci break; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* Sound/brightness button pressed */ 143062306a36Sopenharmony_ci case PMU_INT_SNDBRT: 143162306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 143262306a36Sopenharmony_ci if (len == 3) 143362306a36Sopenharmony_ci pmac_backlight_set_legacy_brightness_pmu(data[1] >> 4); 143462306a36Sopenharmony_ci#endif 143562306a36Sopenharmony_ci break; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* Tick interrupt */ 143862306a36Sopenharmony_ci case PMU_INT_TICK: 143962306a36Sopenharmony_ci /* Environment or tick interrupt, query batteries */ 144062306a36Sopenharmony_ci if (pmu_battery_count) { 144162306a36Sopenharmony_ci if ((--query_batt_timer) == 0) { 144262306a36Sopenharmony_ci query_battery_state(); 144362306a36Sopenharmony_ci query_batt_timer = BATTERY_POLLING_COUNT; 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci break; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci case PMU_INT_ENVIRONMENT: 144962306a36Sopenharmony_ci if (pmu_battery_count) 145062306a36Sopenharmony_ci query_battery_state(); 145162306a36Sopenharmony_ci pmu_pass_intr(data, len); 145262306a36Sopenharmony_ci /* len == 6 is probably a bad check. But how do I 145362306a36Sopenharmony_ci * know what PMU versions send what events here? */ 145462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ADB_PMU_EVENT) && len == 6) { 145562306a36Sopenharmony_ci via_pmu_event(PMU_EVT_POWER, !!(data[1]&8)); 145662306a36Sopenharmony_ci via_pmu_event(PMU_EVT_LID, data[1]&1); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci break; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci default: 146162306a36Sopenharmony_ci pmu_pass_intr(data, len); 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci goto next; 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_cistatic struct adb_request* 146762306a36Sopenharmony_cipmu_sr_intr(void) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct adb_request *req; 147062306a36Sopenharmony_ci int bite = 0; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (in_8(&via2[B]) & TREQ) { 147362306a36Sopenharmony_ci printk(KERN_ERR "PMU: spurious SR intr (%x)\n", in_8(&via2[B])); 147462306a36Sopenharmony_ci return NULL; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci /* The ack may not yet be low when we get the interrupt */ 147762306a36Sopenharmony_ci while ((in_8(&via2[B]) & TACK) != 0) 147862306a36Sopenharmony_ci ; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci /* if reading grab the byte, and reset the interrupt */ 148162306a36Sopenharmony_ci if (pmu_state == reading || pmu_state == reading_intr) 148262306a36Sopenharmony_ci bite = in_8(&via1[SR]); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* reset TREQ and wait for TACK to go high */ 148562306a36Sopenharmony_ci out_8(&via2[B], in_8(&via2[B]) | TREQ); 148662306a36Sopenharmony_ci wait_for_ack(); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci switch (pmu_state) { 148962306a36Sopenharmony_ci case sending: 149062306a36Sopenharmony_ci req = current_req; 149162306a36Sopenharmony_ci if (data_len < 0) { 149262306a36Sopenharmony_ci data_len = req->nbytes - 1; 149362306a36Sopenharmony_ci send_byte(data_len); 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci if (data_index <= data_len) { 149762306a36Sopenharmony_ci send_byte(req->data[data_index++]); 149862306a36Sopenharmony_ci break; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci req->sent = 1; 150162306a36Sopenharmony_ci data_len = pmu_data_len[req->data[0]][1]; 150262306a36Sopenharmony_ci if (data_len == 0) { 150362306a36Sopenharmony_ci pmu_state = idle; 150462306a36Sopenharmony_ci current_req = req->next; 150562306a36Sopenharmony_ci if (req->reply_expected) 150662306a36Sopenharmony_ci req_awaiting_reply = req; 150762306a36Sopenharmony_ci else 150862306a36Sopenharmony_ci return req; 150962306a36Sopenharmony_ci } else { 151062306a36Sopenharmony_ci pmu_state = reading; 151162306a36Sopenharmony_ci data_index = 0; 151262306a36Sopenharmony_ci reply_ptr = req->reply + req->reply_len; 151362306a36Sopenharmony_ci recv_byte(); 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci break; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci case intack: 151862306a36Sopenharmony_ci data_index = 0; 151962306a36Sopenharmony_ci data_len = -1; 152062306a36Sopenharmony_ci pmu_state = reading_intr; 152162306a36Sopenharmony_ci reply_ptr = interrupt_data[int_data_last]; 152262306a36Sopenharmony_ci recv_byte(); 152362306a36Sopenharmony_ci if (gpio_irq >= 0 && !gpio_irq_enabled) { 152462306a36Sopenharmony_ci enable_irq(gpio_irq); 152562306a36Sopenharmony_ci gpio_irq_enabled = 1; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci break; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci case reading: 153062306a36Sopenharmony_ci case reading_intr: 153162306a36Sopenharmony_ci if (data_len == -1) { 153262306a36Sopenharmony_ci data_len = bite; 153362306a36Sopenharmony_ci if (bite > 32) 153462306a36Sopenharmony_ci printk(KERN_ERR "PMU: bad reply len %d\n", bite); 153562306a36Sopenharmony_ci } else if (data_index < 32) { 153662306a36Sopenharmony_ci reply_ptr[data_index++] = bite; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci if (data_index < data_len) { 153962306a36Sopenharmony_ci recv_byte(); 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (pmu_state == reading_intr) { 154462306a36Sopenharmony_ci pmu_state = idle; 154562306a36Sopenharmony_ci int_data_state[int_data_last] = int_data_ready; 154662306a36Sopenharmony_ci interrupt_data_len[int_data_last] = data_len; 154762306a36Sopenharmony_ci } else { 154862306a36Sopenharmony_ci req = current_req; 154962306a36Sopenharmony_ci /* 155062306a36Sopenharmony_ci * For PMU sleep and freq change requests, we lock the 155162306a36Sopenharmony_ci * PMU until it's explicitly unlocked. This avoids any 155262306a36Sopenharmony_ci * spurrious event polling getting in 155362306a36Sopenharmony_ci */ 155462306a36Sopenharmony_ci current_req = req->next; 155562306a36Sopenharmony_ci req->reply_len += data_index; 155662306a36Sopenharmony_ci if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED) 155762306a36Sopenharmony_ci pmu_state = locked; 155862306a36Sopenharmony_ci else 155962306a36Sopenharmony_ci pmu_state = idle; 156062306a36Sopenharmony_ci return req; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci default: 156562306a36Sopenharmony_ci printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n", 156662306a36Sopenharmony_ci pmu_state); 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci return NULL; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic irqreturn_t 157262306a36Sopenharmony_civia_pmu_interrupt(int irq, void *arg) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci unsigned long flags; 157562306a36Sopenharmony_ci int intr; 157662306a36Sopenharmony_ci int nloop = 0; 157762306a36Sopenharmony_ci int int_data = -1; 157862306a36Sopenharmony_ci struct adb_request *req = NULL; 157962306a36Sopenharmony_ci int handled = 0; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci /* This is a bit brutal, we can probably do better */ 158262306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 158362306a36Sopenharmony_ci ++disable_poll; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci for (;;) { 158662306a36Sopenharmony_ci /* On 68k Macs, VIA interrupts are dispatched individually. 158762306a36Sopenharmony_ci * Unless we are polling, the relevant IRQ flag has already 158862306a36Sopenharmony_ci * been cleared. 158962306a36Sopenharmony_ci */ 159062306a36Sopenharmony_ci intr = 0; 159162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_PMAC) || !irq) { 159262306a36Sopenharmony_ci intr = in_8(&via1[IFR]) & (SR_INT | CB1_INT); 159362306a36Sopenharmony_ci out_8(&via1[IFR], intr); 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 159662306a36Sopenharmony_ci switch (irq) { 159762306a36Sopenharmony_ci case IRQ_MAC_ADB_CL: 159862306a36Sopenharmony_ci intr = CB1_INT; 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci case IRQ_MAC_ADB_SR: 160162306a36Sopenharmony_ci intr = SR_INT; 160262306a36Sopenharmony_ci break; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci#endif 160562306a36Sopenharmony_ci if (intr == 0) 160662306a36Sopenharmony_ci break; 160762306a36Sopenharmony_ci handled = 1; 160862306a36Sopenharmony_ci if (++nloop > 1000) { 160962306a36Sopenharmony_ci printk(KERN_DEBUG "PMU: stuck in intr loop, " 161062306a36Sopenharmony_ci "intr=%x, ier=%x pmu_state=%d\n", 161162306a36Sopenharmony_ci intr, in_8(&via1[IER]), pmu_state); 161262306a36Sopenharmony_ci break; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci if (intr & CB1_INT) { 161562306a36Sopenharmony_ci adb_int_pending = 1; 161662306a36Sopenharmony_ci pmu_irq_stats[11]++; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci if (intr & SR_INT) { 161962306a36Sopenharmony_ci req = pmu_sr_intr(); 162062306a36Sopenharmony_ci if (req) 162162306a36Sopenharmony_ci break; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 162462306a36Sopenharmony_ci break; 162562306a36Sopenharmony_ci#endif 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cirecheck: 162962306a36Sopenharmony_ci if (pmu_state == idle) { 163062306a36Sopenharmony_ci if (adb_int_pending) { 163162306a36Sopenharmony_ci if (int_data_state[0] == int_data_empty) 163262306a36Sopenharmony_ci int_data_last = 0; 163362306a36Sopenharmony_ci else if (int_data_state[1] == int_data_empty) 163462306a36Sopenharmony_ci int_data_last = 1; 163562306a36Sopenharmony_ci else 163662306a36Sopenharmony_ci goto no_free_slot; 163762306a36Sopenharmony_ci pmu_state = intack; 163862306a36Sopenharmony_ci int_data_state[int_data_last] = int_data_fill; 163962306a36Sopenharmony_ci /* Sounds safer to make sure ACK is high before writing. 164062306a36Sopenharmony_ci * This helped kill a problem with ADB and some iBooks 164162306a36Sopenharmony_ci */ 164262306a36Sopenharmony_ci wait_for_ack(); 164362306a36Sopenharmony_ci send_byte(PMU_INT_ACK); 164462306a36Sopenharmony_ci adb_int_pending = 0; 164562306a36Sopenharmony_ci } else if (current_req) 164662306a36Sopenharmony_ci pmu_start(); 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_cino_free_slot: 164962306a36Sopenharmony_ci /* Mark the oldest buffer for flushing */ 165062306a36Sopenharmony_ci if (int_data_state[!int_data_last] == int_data_ready) { 165162306a36Sopenharmony_ci int_data_state[!int_data_last] = int_data_flush; 165262306a36Sopenharmony_ci int_data = !int_data_last; 165362306a36Sopenharmony_ci } else if (int_data_state[int_data_last] == int_data_ready) { 165462306a36Sopenharmony_ci int_data_state[int_data_last] = int_data_flush; 165562306a36Sopenharmony_ci int_data = int_data_last; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci --disable_poll; 165862306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* Deal with completed PMU requests outside of the lock */ 166162306a36Sopenharmony_ci if (req) { 166262306a36Sopenharmony_ci pmu_done(req); 166362306a36Sopenharmony_ci req = NULL; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* Deal with interrupt datas outside of the lock */ 166762306a36Sopenharmony_ci if (int_data >= 0) { 166862306a36Sopenharmony_ci pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data]); 166962306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 167062306a36Sopenharmony_ci ++disable_poll; 167162306a36Sopenharmony_ci int_data_state[int_data] = int_data_empty; 167262306a36Sopenharmony_ci int_data = -1; 167362306a36Sopenharmony_ci goto recheck; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci return IRQ_RETVAL(handled); 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_civoid 168062306a36Sopenharmony_cipmu_unlock(void) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci unsigned long flags; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 168562306a36Sopenharmony_ci if (pmu_state == locked) 168662306a36Sopenharmony_ci pmu_state = idle; 168762306a36Sopenharmony_ci adb_int_pending = 1; 168862306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_cistatic __maybe_unused irqreturn_t 169362306a36Sopenharmony_cigpio1_interrupt(int irq, void *arg) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci unsigned long flags; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { 169862306a36Sopenharmony_ci spin_lock_irqsave(&pmu_lock, flags); 169962306a36Sopenharmony_ci if (gpio_irq_enabled > 0) { 170062306a36Sopenharmony_ci disable_irq_nosync(gpio_irq); 170162306a36Sopenharmony_ci gpio_irq_enabled = 0; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci pmu_irq_stats[12]++; 170462306a36Sopenharmony_ci adb_int_pending = 1; 170562306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_lock, flags); 170662306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 170762306a36Sopenharmony_ci return IRQ_HANDLED; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci return IRQ_NONE; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_civoid 171362306a36Sopenharmony_cipmu_enable_irled(int on) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct adb_request req; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (pmu_state == uninitialized) 171862306a36Sopenharmony_ci return ; 171962306a36Sopenharmony_ci if (pmu_kind == PMU_KEYLARGO_BASED) 172062306a36Sopenharmony_ci return ; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED | 172362306a36Sopenharmony_ci (on ? PMU_POW_ON : PMU_POW_OFF)); 172462306a36Sopenharmony_ci pmu_wait_complete(&req); 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci/* Offset between Unix time (1970-based) and Mac time (1904-based) */ 172862306a36Sopenharmony_ci#define RTC_OFFSET 2082844800 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_citime64_t pmu_get_time(void) 173162306a36Sopenharmony_ci{ 173262306a36Sopenharmony_ci struct adb_request req; 173362306a36Sopenharmony_ci u32 now; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci pmu_wait_complete(&req); 173862306a36Sopenharmony_ci if (req.reply_len != 4) 173962306a36Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 174062306a36Sopenharmony_ci now = (req.reply[0] << 24) + (req.reply[1] << 16) + 174162306a36Sopenharmony_ci (req.reply[2] << 8) + req.reply[3]; 174262306a36Sopenharmony_ci return (time64_t)now - RTC_OFFSET; 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ciint pmu_set_rtc_time(struct rtc_time *tm) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci u32 now; 174862306a36Sopenharmony_ci struct adb_request req; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci now = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET); 175162306a36Sopenharmony_ci if (pmu_request(&req, NULL, 5, PMU_SET_RTC, 175262306a36Sopenharmony_ci now >> 24, now >> 16, now >> 8, now) < 0) 175362306a36Sopenharmony_ci return -ENXIO; 175462306a36Sopenharmony_ci pmu_wait_complete(&req); 175562306a36Sopenharmony_ci if (req.reply_len != 0) 175662306a36Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 175762306a36Sopenharmony_ci return 0; 175862306a36Sopenharmony_ci} 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_civoid 176162306a36Sopenharmony_cipmu_restart(void) 176262306a36Sopenharmony_ci{ 176362306a36Sopenharmony_ci struct adb_request req; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (pmu_state == uninitialized) 176662306a36Sopenharmony_ci return; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci local_irq_disable(); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci drop_interrupts = 1; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) { 177362306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | 177462306a36Sopenharmony_ci PMU_INT_TICK ); 177562306a36Sopenharmony_ci while(!req.complete) 177662306a36Sopenharmony_ci pmu_poll(); 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci pmu_request(&req, NULL, 1, PMU_RESET); 178062306a36Sopenharmony_ci pmu_wait_complete(&req); 178162306a36Sopenharmony_ci for (;;) 178262306a36Sopenharmony_ci ; 178362306a36Sopenharmony_ci} 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_civoid 178662306a36Sopenharmony_cipmu_shutdown(void) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci struct adb_request req; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (pmu_state == uninitialized) 179162306a36Sopenharmony_ci return; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci local_irq_disable(); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci drop_interrupts = 1; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (pmu_kind != PMU_KEYLARGO_BASED) { 179862306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | 179962306a36Sopenharmony_ci PMU_INT_TICK ); 180062306a36Sopenharmony_ci pmu_wait_complete(&req); 180162306a36Sopenharmony_ci } else { 180262306a36Sopenharmony_ci /* Disable server mode on shutdown or we'll just 180362306a36Sopenharmony_ci * wake up again 180462306a36Sopenharmony_ci */ 180562306a36Sopenharmony_ci pmu_set_server_mode(0); 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SHUTDOWN, 180962306a36Sopenharmony_ci 'M', 'A', 'T', 'T'); 181062306a36Sopenharmony_ci pmu_wait_complete(&req); 181162306a36Sopenharmony_ci for (;;) 181262306a36Sopenharmony_ci ; 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ciint 181662306a36Sopenharmony_cipmu_present(void) 181762306a36Sopenharmony_ci{ 181862306a36Sopenharmony_ci return pmu_state != uninitialized; 181962306a36Sopenharmony_ci} 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 182262306a36Sopenharmony_ci/* 182362306a36Sopenharmony_ci * Put the powerbook to sleep. 182462306a36Sopenharmony_ci */ 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_cistatic u32 save_via[8]; 182762306a36Sopenharmony_cistatic int __fake_sleep; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic void 183062306a36Sopenharmony_cisave_via_state(void) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci save_via[0] = in_8(&via1[ANH]); 183362306a36Sopenharmony_ci save_via[1] = in_8(&via1[DIRA]); 183462306a36Sopenharmony_ci save_via[2] = in_8(&via1[B]); 183562306a36Sopenharmony_ci save_via[3] = in_8(&via1[DIRB]); 183662306a36Sopenharmony_ci save_via[4] = in_8(&via1[PCR]); 183762306a36Sopenharmony_ci save_via[5] = in_8(&via1[ACR]); 183862306a36Sopenharmony_ci save_via[6] = in_8(&via1[T1CL]); 183962306a36Sopenharmony_ci save_via[7] = in_8(&via1[T1CH]); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_cistatic void 184262306a36Sopenharmony_cirestore_via_state(void) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci out_8(&via1[ANH], save_via[0]); 184562306a36Sopenharmony_ci out_8(&via1[DIRA], save_via[1]); 184662306a36Sopenharmony_ci out_8(&via1[B], save_via[2]); 184762306a36Sopenharmony_ci out_8(&via1[DIRB], save_via[3]); 184862306a36Sopenharmony_ci out_8(&via1[PCR], save_via[4]); 184962306a36Sopenharmony_ci out_8(&via1[ACR], save_via[5]); 185062306a36Sopenharmony_ci out_8(&via1[T1CL], save_via[6]); 185162306a36Sopenharmony_ci out_8(&via1[T1CH], save_via[7]); 185262306a36Sopenharmony_ci out_8(&via1[IER], IER_CLR | 0x7f); /* disable all intrs */ 185362306a36Sopenharmony_ci out_8(&via1[IFR], 0x7f); /* clear IFR */ 185462306a36Sopenharmony_ci out_8(&via1[IER], IER_SET | SR_INT | CB1_INT); 185562306a36Sopenharmony_ci} 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci#define GRACKLE_PM (1<<7) 185862306a36Sopenharmony_ci#define GRACKLE_DOZE (1<<5) 185962306a36Sopenharmony_ci#define GRACKLE_NAP (1<<4) 186062306a36Sopenharmony_ci#define GRACKLE_SLEEP (1<<3) 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cistatic int powerbook_sleep_grackle(void) 186362306a36Sopenharmony_ci{ 186462306a36Sopenharmony_ci unsigned long save_l2cr; 186562306a36Sopenharmony_ci unsigned short pmcr1; 186662306a36Sopenharmony_ci struct adb_request req; 186762306a36Sopenharmony_ci struct pci_dev *grackle; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci grackle = pci_get_domain_bus_and_slot(0, 0, 0); 187062306a36Sopenharmony_ci if (!grackle) 187162306a36Sopenharmony_ci return -ENODEV; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* Turn off various things. Darwin does some retry tests here... */ 187462306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE); 187562306a36Sopenharmony_ci pmu_wait_complete(&req); 187662306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 187762306a36Sopenharmony_ci PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY); 187862306a36Sopenharmony_ci pmu_wait_complete(&req); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* For 750, save backside cache setting and disable it */ 188162306a36Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci if (!__fake_sleep) { 188462306a36Sopenharmony_ci /* Ask the PMU to put us to sleep */ 188562306a36Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 188662306a36Sopenharmony_ci pmu_wait_complete(&req); 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci /* The VIA is supposed not to be restored correctly*/ 189062306a36Sopenharmony_ci save_via_state(); 189162306a36Sopenharmony_ci /* We shut down some HW */ 189262306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci pci_read_config_word(grackle, 0x70, &pmcr1); 189562306a36Sopenharmony_ci /* Apparently, MacOS uses NAP mode for Grackle ??? */ 189662306a36Sopenharmony_ci pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); 189762306a36Sopenharmony_ci pmcr1 |= GRACKLE_PM|GRACKLE_NAP; 189862306a36Sopenharmony_ci pci_write_config_word(grackle, 0x70, pmcr1); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* Call low-level ASM sleep handler */ 190162306a36Sopenharmony_ci if (__fake_sleep) 190262306a36Sopenharmony_ci mdelay(5000); 190362306a36Sopenharmony_ci else 190462306a36Sopenharmony_ci low_sleep_handler(); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci /* We're awake again, stop grackle PM */ 190762306a36Sopenharmony_ci pci_read_config_word(grackle, 0x70, &pmcr1); 190862306a36Sopenharmony_ci pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); 190962306a36Sopenharmony_ci pci_write_config_word(grackle, 0x70, pmcr1); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci pci_dev_put(grackle); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci /* Make sure the PMU is idle */ 191462306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); 191562306a36Sopenharmony_ci restore_via_state(); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* Restore L2 cache */ 191862306a36Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 191962306a36Sopenharmony_ci _set_L2CR(save_l2cr); 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* Restore userland MMU context */ 192262306a36Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* Power things up */ 192562306a36Sopenharmony_ci pmu_unlock(); 192662306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 192762306a36Sopenharmony_ci pmu_wait_complete(&req); 192862306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, 192962306a36Sopenharmony_ci PMU_POW0_ON|PMU_POW0_HARD_DRIVE); 193062306a36Sopenharmony_ci pmu_wait_complete(&req); 193162306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 193262306a36Sopenharmony_ci PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY); 193362306a36Sopenharmony_ci pmu_wait_complete(&req); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci return 0; 193662306a36Sopenharmony_ci} 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_cistatic int 193962306a36Sopenharmony_cipowerbook_sleep_Core99(void) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci unsigned long save_l2cr; 194262306a36Sopenharmony_ci unsigned long save_l3cr; 194362306a36Sopenharmony_ci struct adb_request req; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) { 194662306a36Sopenharmony_ci printk(KERN_ERR "Sleep mode not supported on this machine\n"); 194762306a36Sopenharmony_ci return -ENOSYS; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci if (num_online_cpus() > 1 || cpu_is_offline(0)) 195162306a36Sopenharmony_ci return -EAGAIN; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci /* Stop environment and ADB interrupts */ 195462306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0); 195562306a36Sopenharmony_ci pmu_wait_complete(&req); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci /* Tell PMU what events will wake us up */ 195862306a36Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS, 195962306a36Sopenharmony_ci 0xff, 0xff); 196062306a36Sopenharmony_ci pmu_wait_complete(&req); 196162306a36Sopenharmony_ci pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS, 196262306a36Sopenharmony_ci 0, PMU_PWR_WAKEUP_KEY | 196362306a36Sopenharmony_ci (option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0)); 196462306a36Sopenharmony_ci pmu_wait_complete(&req); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* Save the state of the L2 and L3 caches */ 196762306a36Sopenharmony_ci save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ 196862306a36Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci if (!__fake_sleep) { 197162306a36Sopenharmony_ci /* Ask the PMU to put us to sleep */ 197262306a36Sopenharmony_ci pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 197362306a36Sopenharmony_ci pmu_wait_complete(&req); 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* The VIA is supposed not to be restored correctly*/ 197762306a36Sopenharmony_ci save_via_state(); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Shut down various ASICs. There's a chance that we can no longer 198062306a36Sopenharmony_ci * talk to the PMU after this, so I moved it to _after_ sending the 198162306a36Sopenharmony_ci * sleep command to it. Still need to be checked. 198262306a36Sopenharmony_ci */ 198362306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci /* Call low-level ASM sleep handler */ 198662306a36Sopenharmony_ci if (__fake_sleep) 198762306a36Sopenharmony_ci mdelay(5000); 198862306a36Sopenharmony_ci else 198962306a36Sopenharmony_ci low_sleep_handler(); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* Restore Apple core ASICs state */ 199262306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* Restore VIA */ 199562306a36Sopenharmony_ci restore_via_state(); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci /* tweak LPJ before cpufreq is there */ 199862306a36Sopenharmony_ci loops_per_jiffy *= 2; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci /* Restore video */ 200162306a36Sopenharmony_ci pmac_call_early_video_resume(); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* Restore L2 cache */ 200462306a36Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 200562306a36Sopenharmony_ci _set_L2CR(save_l2cr); 200662306a36Sopenharmony_ci /* Restore L3 cache */ 200762306a36Sopenharmony_ci if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) 200862306a36Sopenharmony_ci _set_L3CR(save_l3cr); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci /* Restore userland MMU context */ 201162306a36Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci /* Tell PMU we are ready */ 201462306a36Sopenharmony_ci pmu_unlock(); 201562306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 201662306a36Sopenharmony_ci pmu_wait_complete(&req); 201762306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); 201862306a36Sopenharmony_ci pmu_wait_complete(&req); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* Restore LPJ, cpufreq will adjust the cpu frequency */ 202162306a36Sopenharmony_ci loops_per_jiffy /= 2; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci return 0; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci#define PB3400_MEM_CTRL 0xf8000000 202762306a36Sopenharmony_ci#define PB3400_MEM_CTRL_SLEEP 0x70 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_cistatic void __iomem *pb3400_mem_ctrl; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_cistatic void powerbook_sleep_init_3400(void) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci /* map in the memory controller registers */ 203462306a36Sopenharmony_ci pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100); 203562306a36Sopenharmony_ci if (pb3400_mem_ctrl == NULL) 203662306a36Sopenharmony_ci printk(KERN_WARNING "ioremap failed: sleep won't be possible"); 203762306a36Sopenharmony_ci} 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_cistatic int powerbook_sleep_3400(void) 204062306a36Sopenharmony_ci{ 204162306a36Sopenharmony_ci int i, x; 204262306a36Sopenharmony_ci unsigned int hid0; 204362306a36Sopenharmony_ci unsigned long msr; 204462306a36Sopenharmony_ci struct adb_request sleep_req; 204562306a36Sopenharmony_ci unsigned int __iomem *mem_ctrl_sleep; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (pb3400_mem_ctrl == NULL) 204862306a36Sopenharmony_ci return -ENOMEM; 204962306a36Sopenharmony_ci mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* Set the memory controller to keep the memory refreshed 205262306a36Sopenharmony_ci while we're asleep */ 205362306a36Sopenharmony_ci for (i = 0x403f; i >= 0x4000; --i) { 205462306a36Sopenharmony_ci out_be32(mem_ctrl_sleep, i); 205562306a36Sopenharmony_ci do { 205662306a36Sopenharmony_ci x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff; 205762306a36Sopenharmony_ci } while (x == 0); 205862306a36Sopenharmony_ci if (x >= 0x100) 205962306a36Sopenharmony_ci break; 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci /* Ask the PMU to put us to sleep */ 206362306a36Sopenharmony_ci pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); 206462306a36Sopenharmony_ci pmu_wait_complete(&sleep_req); 206562306a36Sopenharmony_ci pmu_unlock(); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci asleep = 1; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci /* Put the CPU into sleep mode */ 207262306a36Sopenharmony_ci hid0 = mfspr(SPRN_HID0); 207362306a36Sopenharmony_ci hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP; 207462306a36Sopenharmony_ci mtspr(SPRN_HID0, hid0); 207562306a36Sopenharmony_ci local_irq_enable(); 207662306a36Sopenharmony_ci msr = mfmsr() | MSR_POW; 207762306a36Sopenharmony_ci while (asleep) { 207862306a36Sopenharmony_ci mb(); 207962306a36Sopenharmony_ci mtmsr(msr); 208062306a36Sopenharmony_ci isync(); 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci local_irq_disable(); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* OK, we're awake again, start restoring things */ 208562306a36Sopenharmony_ci out_be32(mem_ctrl_sleep, 0x3f); 208662306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci return 0; 208962306a36Sopenharmony_ci} 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci/* 209462306a36Sopenharmony_ci * Support for /dev/pmu device 209562306a36Sopenharmony_ci */ 209662306a36Sopenharmony_ci#define RB_SIZE 0x10 209762306a36Sopenharmony_cistruct pmu_private { 209862306a36Sopenharmony_ci struct list_head list; 209962306a36Sopenharmony_ci int rb_get; 210062306a36Sopenharmony_ci int rb_put; 210162306a36Sopenharmony_ci struct rb_entry { 210262306a36Sopenharmony_ci unsigned short len; 210362306a36Sopenharmony_ci unsigned char data[16]; 210462306a36Sopenharmony_ci } rb_buf[RB_SIZE]; 210562306a36Sopenharmony_ci wait_queue_head_t wait; 210662306a36Sopenharmony_ci spinlock_t lock; 210762306a36Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 210862306a36Sopenharmony_ci int backlight_locker; 210962306a36Sopenharmony_ci#endif 211062306a36Sopenharmony_ci}; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_cistatic LIST_HEAD(all_pmu_pvt); 211362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(all_pvt_lock); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_cistatic void 211662306a36Sopenharmony_cipmu_pass_intr(unsigned char *data, int len) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci struct pmu_private *pp; 211962306a36Sopenharmony_ci struct list_head *list; 212062306a36Sopenharmony_ci int i; 212162306a36Sopenharmony_ci unsigned long flags; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci if (len > sizeof(pp->rb_buf[0].data)) 212462306a36Sopenharmony_ci len = sizeof(pp->rb_buf[0].data); 212562306a36Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 212662306a36Sopenharmony_ci for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) { 212762306a36Sopenharmony_ci pp = list_entry(list, struct pmu_private, list); 212862306a36Sopenharmony_ci spin_lock(&pp->lock); 212962306a36Sopenharmony_ci i = pp->rb_put + 1; 213062306a36Sopenharmony_ci if (i >= RB_SIZE) 213162306a36Sopenharmony_ci i = 0; 213262306a36Sopenharmony_ci if (i != pp->rb_get) { 213362306a36Sopenharmony_ci struct rb_entry *rp = &pp->rb_buf[pp->rb_put]; 213462306a36Sopenharmony_ci rp->len = len; 213562306a36Sopenharmony_ci memcpy(rp->data, data, len); 213662306a36Sopenharmony_ci pp->rb_put = i; 213762306a36Sopenharmony_ci wake_up_interruptible(&pp->wait); 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci spin_unlock(&pp->lock); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 214262306a36Sopenharmony_ci} 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_cistatic int 214562306a36Sopenharmony_cipmu_open(struct inode *inode, struct file *file) 214662306a36Sopenharmony_ci{ 214762306a36Sopenharmony_ci struct pmu_private *pp; 214862306a36Sopenharmony_ci unsigned long flags; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL); 215162306a36Sopenharmony_ci if (!pp) 215262306a36Sopenharmony_ci return -ENOMEM; 215362306a36Sopenharmony_ci pp->rb_get = pp->rb_put = 0; 215462306a36Sopenharmony_ci spin_lock_init(&pp->lock); 215562306a36Sopenharmony_ci init_waitqueue_head(&pp->wait); 215662306a36Sopenharmony_ci mutex_lock(&pmu_info_proc_mutex); 215762306a36Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 215862306a36Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 215962306a36Sopenharmony_ci pp->backlight_locker = 0; 216062306a36Sopenharmony_ci#endif 216162306a36Sopenharmony_ci list_add(&pp->list, &all_pmu_pvt); 216262306a36Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 216362306a36Sopenharmony_ci file->private_data = pp; 216462306a36Sopenharmony_ci mutex_unlock(&pmu_info_proc_mutex); 216562306a36Sopenharmony_ci return 0; 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cistatic ssize_t 216962306a36Sopenharmony_cipmu_read(struct file *file, char __user *buf, 217062306a36Sopenharmony_ci size_t count, loff_t *ppos) 217162306a36Sopenharmony_ci{ 217262306a36Sopenharmony_ci struct pmu_private *pp = file->private_data; 217362306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 217462306a36Sopenharmony_ci unsigned long flags; 217562306a36Sopenharmony_ci int ret = 0; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci if (count < 1 || !pp) 217862306a36Sopenharmony_ci return -EINVAL; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 218162306a36Sopenharmony_ci add_wait_queue(&pp->wait, &wait); 218262306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci for (;;) { 218562306a36Sopenharmony_ci ret = -EAGAIN; 218662306a36Sopenharmony_ci if (pp->rb_get != pp->rb_put) { 218762306a36Sopenharmony_ci int i = pp->rb_get; 218862306a36Sopenharmony_ci struct rb_entry *rp = &pp->rb_buf[i]; 218962306a36Sopenharmony_ci ret = rp->len; 219062306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 219162306a36Sopenharmony_ci if (ret > count) 219262306a36Sopenharmony_ci ret = count; 219362306a36Sopenharmony_ci if (ret > 0 && copy_to_user(buf, rp->data, ret)) 219462306a36Sopenharmony_ci ret = -EFAULT; 219562306a36Sopenharmony_ci if (++i >= RB_SIZE) 219662306a36Sopenharmony_ci i = 0; 219762306a36Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 219862306a36Sopenharmony_ci pp->rb_get = i; 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci if (ret >= 0) 220162306a36Sopenharmony_ci break; 220262306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 220362306a36Sopenharmony_ci break; 220462306a36Sopenharmony_ci ret = -ERESTARTSYS; 220562306a36Sopenharmony_ci if (signal_pending(current)) 220662306a36Sopenharmony_ci break; 220762306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 220862306a36Sopenharmony_ci schedule(); 220962306a36Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 221062306a36Sopenharmony_ci } 221162306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 221262306a36Sopenharmony_ci remove_wait_queue(&pp->wait, &wait); 221362306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci return ret; 221662306a36Sopenharmony_ci} 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_cistatic ssize_t 221962306a36Sopenharmony_cipmu_write(struct file *file, const char __user *buf, 222062306a36Sopenharmony_ci size_t count, loff_t *ppos) 222162306a36Sopenharmony_ci{ 222262306a36Sopenharmony_ci return 0; 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_cistatic __poll_t 222662306a36Sopenharmony_cipmu_fpoll(struct file *filp, poll_table *wait) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci struct pmu_private *pp = filp->private_data; 222962306a36Sopenharmony_ci __poll_t mask = 0; 223062306a36Sopenharmony_ci unsigned long flags; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci if (!pp) 223362306a36Sopenharmony_ci return 0; 223462306a36Sopenharmony_ci poll_wait(filp, &pp->wait, wait); 223562306a36Sopenharmony_ci spin_lock_irqsave(&pp->lock, flags); 223662306a36Sopenharmony_ci if (pp->rb_get != pp->rb_put) 223762306a36Sopenharmony_ci mask |= EPOLLIN; 223862306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->lock, flags); 223962306a36Sopenharmony_ci return mask; 224062306a36Sopenharmony_ci} 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_cistatic int 224362306a36Sopenharmony_cipmu_release(struct inode *inode, struct file *file) 224462306a36Sopenharmony_ci{ 224562306a36Sopenharmony_ci struct pmu_private *pp = file->private_data; 224662306a36Sopenharmony_ci unsigned long flags; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (pp) { 224962306a36Sopenharmony_ci file->private_data = NULL; 225062306a36Sopenharmony_ci spin_lock_irqsave(&all_pvt_lock, flags); 225162306a36Sopenharmony_ci list_del(&pp->list); 225262306a36Sopenharmony_ci spin_unlock_irqrestore(&all_pvt_lock, flags); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) 225562306a36Sopenharmony_ci if (pp->backlight_locker) 225662306a36Sopenharmony_ci pmac_backlight_enable(); 225762306a36Sopenharmony_ci#endif 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci kfree(pp); 226062306a36Sopenharmony_ci } 226162306a36Sopenharmony_ci return 0; 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 226562306a36Sopenharmony_cistatic void pmac_suspend_disable_irqs(void) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci /* Call platform functions marked "on sleep" */ 226862306a36Sopenharmony_ci pmac_pfunc_i2c_suspend(); 226962306a36Sopenharmony_ci pmac_pfunc_base_suspend(); 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic int powerbook_sleep(suspend_state_t state) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci int error = 0; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci /* Wait for completion of async requests */ 227762306a36Sopenharmony_ci while (!batt_req.complete) 227862306a36Sopenharmony_ci pmu_poll(); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci /* Giveup the lazy FPU & vec so we don't have to back them 228162306a36Sopenharmony_ci * up from the low level code 228262306a36Sopenharmony_ci */ 228362306a36Sopenharmony_ci enable_kernel_fp(); 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 228662306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ALTIVEC)) 228762306a36Sopenharmony_ci enable_kernel_altivec(); 228862306a36Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci switch (pmu_kind) { 229162306a36Sopenharmony_ci case PMU_OHARE_BASED: 229262306a36Sopenharmony_ci error = powerbook_sleep_3400(); 229362306a36Sopenharmony_ci break; 229462306a36Sopenharmony_ci case PMU_HEATHROW_BASED: 229562306a36Sopenharmony_ci case PMU_PADDINGTON_BASED: 229662306a36Sopenharmony_ci error = powerbook_sleep_grackle(); 229762306a36Sopenharmony_ci break; 229862306a36Sopenharmony_ci case PMU_KEYLARGO_BASED: 229962306a36Sopenharmony_ci error = powerbook_sleep_Core99(); 230062306a36Sopenharmony_ci break; 230162306a36Sopenharmony_ci default: 230262306a36Sopenharmony_ci return -ENOSYS; 230362306a36Sopenharmony_ci } 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci if (error) 230662306a36Sopenharmony_ci return error; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci mdelay(100); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci return 0; 231162306a36Sopenharmony_ci} 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_cistatic void pmac_suspend_enable_irqs(void) 231462306a36Sopenharmony_ci{ 231562306a36Sopenharmony_ci /* Force a poll of ADB interrupts */ 231662306a36Sopenharmony_ci adb_int_pending = 1; 231762306a36Sopenharmony_ci via_pmu_interrupt(0, NULL); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci mdelay(10); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci /* Call platform functions marked "on wake" */ 232262306a36Sopenharmony_ci pmac_pfunc_base_resume(); 232362306a36Sopenharmony_ci pmac_pfunc_i2c_resume(); 232462306a36Sopenharmony_ci} 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_cistatic int pmu_sleep_valid(suspend_state_t state) 232762306a36Sopenharmony_ci{ 232862306a36Sopenharmony_ci return state == PM_SUSPEND_MEM 232962306a36Sopenharmony_ci && (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0); 233062306a36Sopenharmony_ci} 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_cistatic const struct platform_suspend_ops pmu_pm_ops = { 233362306a36Sopenharmony_ci .enter = powerbook_sleep, 233462306a36Sopenharmony_ci .valid = pmu_sleep_valid, 233562306a36Sopenharmony_ci}; 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_cistatic int register_pmu_pm_ops(void) 233862306a36Sopenharmony_ci{ 233962306a36Sopenharmony_ci if (pmu_kind == PMU_OHARE_BASED) 234062306a36Sopenharmony_ci powerbook_sleep_init_3400(); 234162306a36Sopenharmony_ci ppc_md.suspend_disable_irqs = pmac_suspend_disable_irqs; 234262306a36Sopenharmony_ci ppc_md.suspend_enable_irqs = pmac_suspend_enable_irqs; 234362306a36Sopenharmony_ci suspend_set_ops(&pmu_pm_ops); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci return 0; 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cidevice_initcall(register_pmu_pm_ops); 234962306a36Sopenharmony_ci#endif 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_cistatic int pmu_ioctl(struct file *filp, 235262306a36Sopenharmony_ci u_int cmd, u_long arg) 235362306a36Sopenharmony_ci{ 235462306a36Sopenharmony_ci __u32 __user *argp = (__u32 __user *)arg; 235562306a36Sopenharmony_ci int error = -EINVAL; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci switch (cmd) { 235862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 235962306a36Sopenharmony_ci case PMU_IOC_SLEEP: 236062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 236162306a36Sopenharmony_ci return -EACCES; 236262306a36Sopenharmony_ci return pm_suspend(PM_SUSPEND_MEM); 236362306a36Sopenharmony_ci case PMU_IOC_CAN_SLEEP: 236462306a36Sopenharmony_ci if (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) < 0) 236562306a36Sopenharmony_ci return put_user(0, argp); 236662306a36Sopenharmony_ci else 236762306a36Sopenharmony_ci return put_user(1, argp); 236862306a36Sopenharmony_ci#endif 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY 237162306a36Sopenharmony_ci /* Compatibility ioctl's for backlight */ 237262306a36Sopenharmony_ci case PMU_IOC_GET_BACKLIGHT: 237362306a36Sopenharmony_ci { 237462306a36Sopenharmony_ci int brightness; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci brightness = pmac_backlight_get_legacy_brightness(); 237762306a36Sopenharmony_ci if (brightness < 0) 237862306a36Sopenharmony_ci return brightness; 237962306a36Sopenharmony_ci else 238062306a36Sopenharmony_ci return put_user(brightness, argp); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci case PMU_IOC_SET_BACKLIGHT: 238462306a36Sopenharmony_ci { 238562306a36Sopenharmony_ci int brightness; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci error = get_user(brightness, argp); 238862306a36Sopenharmony_ci if (error) 238962306a36Sopenharmony_ci return error; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci return pmac_backlight_set_legacy_brightness(brightness); 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci#ifdef CONFIG_INPUT_ADBHID 239462306a36Sopenharmony_ci case PMU_IOC_GRAB_BACKLIGHT: { 239562306a36Sopenharmony_ci struct pmu_private *pp = filp->private_data; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci if (pp->backlight_locker) 239862306a36Sopenharmony_ci return 0; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci pp->backlight_locker = 1; 240162306a36Sopenharmony_ci pmac_backlight_disable(); 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci return 0; 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci#endif /* CONFIG_INPUT_ADBHID */ 240662306a36Sopenharmony_ci#endif /* CONFIG_PMAC_BACKLIGHT_LEGACY */ 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci case PMU_IOC_GET_MODEL: 240962306a36Sopenharmony_ci return put_user(pmu_kind, argp); 241062306a36Sopenharmony_ci case PMU_IOC_HAS_ADB: 241162306a36Sopenharmony_ci return put_user(pmu_has_adb, argp); 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci return error; 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic long pmu_unlocked_ioctl(struct file *filp, 241762306a36Sopenharmony_ci u_int cmd, u_long arg) 241862306a36Sopenharmony_ci{ 241962306a36Sopenharmony_ci int ret; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci mutex_lock(&pmu_info_proc_mutex); 242262306a36Sopenharmony_ci ret = pmu_ioctl(filp, cmd, arg); 242362306a36Sopenharmony_ci mutex_unlock(&pmu_info_proc_mutex); 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci return ret; 242662306a36Sopenharmony_ci} 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 242962306a36Sopenharmony_ci#define PMU_IOC_GET_BACKLIGHT32 _IOR('B', 1, compat_size_t) 243062306a36Sopenharmony_ci#define PMU_IOC_SET_BACKLIGHT32 _IOW('B', 2, compat_size_t) 243162306a36Sopenharmony_ci#define PMU_IOC_GET_MODEL32 _IOR('B', 3, compat_size_t) 243262306a36Sopenharmony_ci#define PMU_IOC_HAS_ADB32 _IOR('B', 4, compat_size_t) 243362306a36Sopenharmony_ci#define PMU_IOC_CAN_SLEEP32 _IOR('B', 5, compat_size_t) 243462306a36Sopenharmony_ci#define PMU_IOC_GRAB_BACKLIGHT32 _IOR('B', 6, compat_size_t) 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cistatic long compat_pmu_ioctl (struct file *filp, u_int cmd, u_long arg) 243762306a36Sopenharmony_ci{ 243862306a36Sopenharmony_ci switch (cmd) { 243962306a36Sopenharmony_ci case PMU_IOC_SLEEP: 244062306a36Sopenharmony_ci break; 244162306a36Sopenharmony_ci case PMU_IOC_GET_BACKLIGHT32: 244262306a36Sopenharmony_ci cmd = PMU_IOC_GET_BACKLIGHT; 244362306a36Sopenharmony_ci break; 244462306a36Sopenharmony_ci case PMU_IOC_SET_BACKLIGHT32: 244562306a36Sopenharmony_ci cmd = PMU_IOC_SET_BACKLIGHT; 244662306a36Sopenharmony_ci break; 244762306a36Sopenharmony_ci case PMU_IOC_GET_MODEL32: 244862306a36Sopenharmony_ci cmd = PMU_IOC_GET_MODEL; 244962306a36Sopenharmony_ci break; 245062306a36Sopenharmony_ci case PMU_IOC_HAS_ADB32: 245162306a36Sopenharmony_ci cmd = PMU_IOC_HAS_ADB; 245262306a36Sopenharmony_ci break; 245362306a36Sopenharmony_ci case PMU_IOC_CAN_SLEEP32: 245462306a36Sopenharmony_ci cmd = PMU_IOC_CAN_SLEEP; 245562306a36Sopenharmony_ci break; 245662306a36Sopenharmony_ci case PMU_IOC_GRAB_BACKLIGHT32: 245762306a36Sopenharmony_ci cmd = PMU_IOC_GRAB_BACKLIGHT; 245862306a36Sopenharmony_ci break; 245962306a36Sopenharmony_ci default: 246062306a36Sopenharmony_ci return -ENOIOCTLCMD; 246162306a36Sopenharmony_ci } 246262306a36Sopenharmony_ci return pmu_unlocked_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); 246362306a36Sopenharmony_ci} 246462306a36Sopenharmony_ci#endif 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_cistatic const struct file_operations pmu_device_fops = { 246762306a36Sopenharmony_ci .read = pmu_read, 246862306a36Sopenharmony_ci .write = pmu_write, 246962306a36Sopenharmony_ci .poll = pmu_fpoll, 247062306a36Sopenharmony_ci .unlocked_ioctl = pmu_unlocked_ioctl, 247162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 247262306a36Sopenharmony_ci .compat_ioctl = compat_pmu_ioctl, 247362306a36Sopenharmony_ci#endif 247462306a36Sopenharmony_ci .open = pmu_open, 247562306a36Sopenharmony_ci .release = pmu_release, 247662306a36Sopenharmony_ci .llseek = noop_llseek, 247762306a36Sopenharmony_ci}; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic struct miscdevice pmu_device = { 248062306a36Sopenharmony_ci PMU_MINOR, "pmu", &pmu_device_fops 248162306a36Sopenharmony_ci}; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_cistatic int pmu_device_init(void) 248462306a36Sopenharmony_ci{ 248562306a36Sopenharmony_ci if (pmu_state == uninitialized) 248662306a36Sopenharmony_ci return 0; 248762306a36Sopenharmony_ci if (misc_register(&pmu_device) < 0) 248862306a36Sopenharmony_ci printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 248962306a36Sopenharmony_ci return 0; 249062306a36Sopenharmony_ci} 249162306a36Sopenharmony_cidevice_initcall(pmu_device_init); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci#ifdef DEBUG_SLEEP 249562306a36Sopenharmony_cistatic inline void 249662306a36Sopenharmony_cipolled_handshake(void) 249762306a36Sopenharmony_ci{ 249862306a36Sopenharmony_ci via2[B] &= ~TREQ; eieio(); 249962306a36Sopenharmony_ci while ((via2[B] & TACK) != 0) 250062306a36Sopenharmony_ci ; 250162306a36Sopenharmony_ci via2[B] |= TREQ; eieio(); 250262306a36Sopenharmony_ci while ((via2[B] & TACK) == 0) 250362306a36Sopenharmony_ci ; 250462306a36Sopenharmony_ci} 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_cistatic inline void 250762306a36Sopenharmony_cipolled_send_byte(int x) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci via1[ACR] |= SR_OUT | SR_EXT; eieio(); 251062306a36Sopenharmony_ci via1[SR] = x; eieio(); 251162306a36Sopenharmony_ci polled_handshake(); 251262306a36Sopenharmony_ci} 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_cistatic inline int 251562306a36Sopenharmony_cipolled_recv_byte(void) 251662306a36Sopenharmony_ci{ 251762306a36Sopenharmony_ci int x; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci via1[ACR] = (via1[ACR] & ~SR_OUT) | SR_EXT; eieio(); 252062306a36Sopenharmony_ci x = via1[SR]; eieio(); 252162306a36Sopenharmony_ci polled_handshake(); 252262306a36Sopenharmony_ci x = via1[SR]; eieio(); 252362306a36Sopenharmony_ci return x; 252462306a36Sopenharmony_ci} 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ciint 252762306a36Sopenharmony_cipmu_polled_request(struct adb_request *req) 252862306a36Sopenharmony_ci{ 252962306a36Sopenharmony_ci unsigned long flags; 253062306a36Sopenharmony_ci int i, l, c; 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci req->complete = 1; 253362306a36Sopenharmony_ci c = req->data[0]; 253462306a36Sopenharmony_ci l = pmu_data_len[c][0]; 253562306a36Sopenharmony_ci if (l >= 0 && req->nbytes != l + 1) 253662306a36Sopenharmony_ci return -EINVAL; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci local_irq_save(flags); 253962306a36Sopenharmony_ci while (pmu_state != idle) 254062306a36Sopenharmony_ci pmu_poll(); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci while ((via2[B] & TACK) == 0) 254362306a36Sopenharmony_ci ; 254462306a36Sopenharmony_ci polled_send_byte(c); 254562306a36Sopenharmony_ci if (l < 0) { 254662306a36Sopenharmony_ci l = req->nbytes - 1; 254762306a36Sopenharmony_ci polled_send_byte(l); 254862306a36Sopenharmony_ci } 254962306a36Sopenharmony_ci for (i = 1; i <= l; ++i) 255062306a36Sopenharmony_ci polled_send_byte(req->data[i]); 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci l = pmu_data_len[c][1]; 255362306a36Sopenharmony_ci if (l < 0) 255462306a36Sopenharmony_ci l = polled_recv_byte(); 255562306a36Sopenharmony_ci for (i = 0; i < l; ++i) 255662306a36Sopenharmony_ci req->reply[i + req->reply_len] = polled_recv_byte(); 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci if (req->done) 255962306a36Sopenharmony_ci (*req->done)(req); 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci local_irq_restore(flags); 256262306a36Sopenharmony_ci return 0; 256362306a36Sopenharmony_ci} 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci/* N.B. This doesn't work on the 3400 */ 256662306a36Sopenharmony_civoid pmu_blink(int n) 256762306a36Sopenharmony_ci{ 256862306a36Sopenharmony_ci struct adb_request req; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci for (; n > 0; --n) { 257362306a36Sopenharmony_ci req.nbytes = 4; 257462306a36Sopenharmony_ci req.done = NULL; 257562306a36Sopenharmony_ci req.data[0] = 0xee; 257662306a36Sopenharmony_ci req.data[1] = 4; 257762306a36Sopenharmony_ci req.data[2] = 0; 257862306a36Sopenharmony_ci req.data[3] = 1; 257962306a36Sopenharmony_ci req.reply[0] = ADB_RET_OK; 258062306a36Sopenharmony_ci req.reply_len = 1; 258162306a36Sopenharmony_ci req.reply_expected = 0; 258262306a36Sopenharmony_ci pmu_polled_request(&req); 258362306a36Sopenharmony_ci mdelay(50); 258462306a36Sopenharmony_ci req.nbytes = 4; 258562306a36Sopenharmony_ci req.done = NULL; 258662306a36Sopenharmony_ci req.data[0] = 0xee; 258762306a36Sopenharmony_ci req.data[1] = 4; 258862306a36Sopenharmony_ci req.data[2] = 0; 258962306a36Sopenharmony_ci req.data[3] = 0; 259062306a36Sopenharmony_ci req.reply[0] = ADB_RET_OK; 259162306a36Sopenharmony_ci req.reply_len = 1; 259262306a36Sopenharmony_ci req.reply_expected = 0; 259362306a36Sopenharmony_ci pmu_polled_request(&req); 259462306a36Sopenharmony_ci mdelay(50); 259562306a36Sopenharmony_ci } 259662306a36Sopenharmony_ci mdelay(50); 259762306a36Sopenharmony_ci} 259862306a36Sopenharmony_ci#endif /* DEBUG_SLEEP */ 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) 260162306a36Sopenharmony_ciint pmu_sys_suspended; 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_cistatic int pmu_syscore_suspend(void) 260462306a36Sopenharmony_ci{ 260562306a36Sopenharmony_ci /* Suspend PMU event interrupts */ 260662306a36Sopenharmony_ci pmu_suspend(); 260762306a36Sopenharmony_ci pmu_sys_suspended = 1; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 261062306a36Sopenharmony_ci /* Tell backlight code not to muck around with the chip anymore */ 261162306a36Sopenharmony_ci pmu_backlight_set_sleep(1); 261262306a36Sopenharmony_ci#endif 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci return 0; 261562306a36Sopenharmony_ci} 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_cistatic void pmu_syscore_resume(void) 261862306a36Sopenharmony_ci{ 261962306a36Sopenharmony_ci struct adb_request req; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci if (!pmu_sys_suspended) 262262306a36Sopenharmony_ci return; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci /* Tell PMU we are ready */ 262562306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); 262662306a36Sopenharmony_ci pmu_wait_complete(&req); 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 262962306a36Sopenharmony_ci /* Tell backlight code it can use the chip again */ 263062306a36Sopenharmony_ci pmu_backlight_set_sleep(0); 263162306a36Sopenharmony_ci#endif 263262306a36Sopenharmony_ci /* Resume PMU event interrupts */ 263362306a36Sopenharmony_ci pmu_resume(); 263462306a36Sopenharmony_ci pmu_sys_suspended = 0; 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_cistatic struct syscore_ops pmu_syscore_ops = { 263862306a36Sopenharmony_ci .suspend = pmu_syscore_suspend, 263962306a36Sopenharmony_ci .resume = pmu_syscore_resume, 264062306a36Sopenharmony_ci}; 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_cistatic int pmu_syscore_register(void) 264362306a36Sopenharmony_ci{ 264462306a36Sopenharmony_ci register_syscore_ops(&pmu_syscore_ops); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci return 0; 264762306a36Sopenharmony_ci} 264862306a36Sopenharmony_cisubsys_initcall(pmu_syscore_register); 264962306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_request); 265262306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_queue_request); 265362306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_poll); 265462306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_poll_adb); 265562306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_wait_complete); 265662306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_suspend); 265762306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_resume); 265862306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_unlock); 265962306a36Sopenharmony_ci#if defined(CONFIG_PPC32) 266062306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_enable_irled); 266162306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_battery_count); 266262306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_batteries); 266362306a36Sopenharmony_ciEXPORT_SYMBOL(pmu_power_flags); 266462306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ 266562306a36Sopenharmony_ci 2666