Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.15-rc9 237 lines 5.8 kB view raw
1/* 2 * Power button driver for Intel MID platforms. 3 * 4 * Copyright (C) 2010,2017 Intel Corp 5 * 6 * Author: Hong Liu <hong.liu@intel.com> 7 * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; version 2 of the License. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 */ 18 19#include <linux/init.h> 20#include <linux/input.h> 21#include <linux/interrupt.h> 22#include <linux/mfd/intel_msic.h> 23#include <linux/module.h> 24#include <linux/platform_device.h> 25#include <linux/pm_wakeirq.h> 26#include <linux/slab.h> 27 28#include <asm/cpu_device_id.h> 29#include <asm/intel-family.h> 30#include <asm/intel_scu_ipc.h> 31 32#define DRIVER_NAME "msic_power_btn" 33 34#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ 35 36/* 37 * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask 38 * power button interrupt 39 */ 40#define MSIC_PWRBTNM (1 << 0) 41 42/* Intel Tangier */ 43#define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */ 44 45/* Basin Cove PMIC */ 46#define BCOVE_PBIRQ 0x02 47#define BCOVE_IRQLVL1MSK 0x0c 48#define BCOVE_PBIRQMASK 0x0d 49#define BCOVE_PBSTATUS 0x27 50 51struct mid_pb_ddata { 52 struct device *dev; 53 int irq; 54 struct input_dev *input; 55 unsigned short mirqlvl1_addr; 56 unsigned short pbstat_addr; 57 u8 pbstat_mask; 58 int (*setup)(struct mid_pb_ddata *ddata); 59}; 60 61static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) 62{ 63 struct input_dev *input = ddata->input; 64 int ret; 65 u8 pbstat; 66 67 ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat); 68 if (ret) 69 return ret; 70 71 dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); 72 73 *value = !(pbstat & ddata->pbstat_mask); 74 return 0; 75} 76 77static int mid_irq_ack(struct mid_pb_ddata *ddata) 78{ 79 return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM); 80} 81 82static int mrfld_setup(struct mid_pb_ddata *ddata) 83{ 84 /* Unmask the PBIRQ and MPBIRQ on Tangier */ 85 intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); 86 intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); 87 88 return 0; 89} 90 91static irqreturn_t mid_pb_isr(int irq, void *dev_id) 92{ 93 struct mid_pb_ddata *ddata = dev_id; 94 struct input_dev *input = ddata->input; 95 int value = 0; 96 int ret; 97 98 ret = mid_pbstat(ddata, &value); 99 if (ret < 0) { 100 dev_err(input->dev.parent, 101 "Read error %d while reading MSIC_PB_STATUS\n", ret); 102 } else { 103 input_event(input, EV_KEY, KEY_POWER, value); 104 input_sync(input); 105 } 106 107 mid_irq_ack(ddata); 108 return IRQ_HANDLED; 109} 110 111static const struct mid_pb_ddata mfld_ddata = { 112 .mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK, 113 .pbstat_addr = INTEL_MSIC_PBSTATUS, 114 .pbstat_mask = MSIC_PB_LEVEL, 115}; 116 117static const struct mid_pb_ddata mrfld_ddata = { 118 .mirqlvl1_addr = BCOVE_IRQLVL1MSK, 119 .pbstat_addr = BCOVE_PBSTATUS, 120 .pbstat_mask = BCOVE_PB_LEVEL, 121 .setup = mrfld_setup, 122}; 123 124#define ICPU(model, ddata) \ 125 { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } 126 127static const struct x86_cpu_id mid_pb_cpu_ids[] = { 128 ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata), 129 ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata), 130 {} 131}; 132 133static int mid_pb_probe(struct platform_device *pdev) 134{ 135 const struct x86_cpu_id *id; 136 struct mid_pb_ddata *ddata; 137 struct input_dev *input; 138 int irq = platform_get_irq(pdev, 0); 139 int error; 140 141 id = x86_match_cpu(mid_pb_cpu_ids); 142 if (!id) 143 return -ENODEV; 144 145 if (irq < 0) { 146 dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); 147 return irq; 148 } 149 150 input = devm_input_allocate_device(&pdev->dev); 151 if (!input) 152 return -ENOMEM; 153 154 input->name = pdev->name; 155 input->phys = "power-button/input0"; 156 input->id.bustype = BUS_HOST; 157 input->dev.parent = &pdev->dev; 158 159 input_set_capability(input, EV_KEY, KEY_POWER); 160 161 ddata = (struct mid_pb_ddata *)id->driver_data; 162 if (!ddata) 163 return -ENODATA; 164 165 ddata->dev = &pdev->dev; 166 ddata->irq = irq; 167 ddata->input = input; 168 169 if (ddata->setup) { 170 error = ddata->setup(ddata); 171 if (error) 172 return error; 173 } 174 175 error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr, 176 IRQF_ONESHOT, DRIVER_NAME, ddata); 177 if (error) { 178 dev_err(&pdev->dev, 179 "Unable to request irq %d for MID power button\n", irq); 180 return error; 181 } 182 183 error = input_register_device(input); 184 if (error) { 185 dev_err(&pdev->dev, 186 "Unable to register input dev, error %d\n", error); 187 return error; 188 } 189 190 platform_set_drvdata(pdev, ddata); 191 192 /* 193 * SCU firmware might send power button interrupts to IA core before 194 * kernel boots and doesn't get EOI from IA core. The first bit of 195 * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new 196 * power interrupt to Android kernel. Unmask the bit when probing 197 * power button in kernel. 198 * There is a very narrow race between irq handler and power button 199 * initialization. The race happens rarely. So we needn't worry 200 * about it. 201 */ 202 error = mid_irq_ack(ddata); 203 if (error) { 204 dev_err(&pdev->dev, 205 "Unable to clear power button interrupt, error: %d\n", 206 error); 207 return error; 208 } 209 210 device_init_wakeup(&pdev->dev, true); 211 dev_pm_set_wake_irq(&pdev->dev, irq); 212 213 return 0; 214} 215 216static int mid_pb_remove(struct platform_device *pdev) 217{ 218 dev_pm_clear_wake_irq(&pdev->dev); 219 device_init_wakeup(&pdev->dev, false); 220 221 return 0; 222} 223 224static struct platform_driver mid_pb_driver = { 225 .driver = { 226 .name = DRIVER_NAME, 227 }, 228 .probe = mid_pb_probe, 229 .remove = mid_pb_remove, 230}; 231 232module_platform_driver(mid_pb_driver); 233 234MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); 235MODULE_DESCRIPTION("Intel MID Power Button Driver"); 236MODULE_LICENSE("GPL v2"); 237MODULE_ALIAS("platform:" DRIVER_NAME);