cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gpio-pmf.c (6269B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Apple Onboard Audio pmf GPIOs
      4 *
      5 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
      6 */
      7
      8#include <linux/slab.h>
      9#include <asm/pmac_feature.h>
     10#include <asm/pmac_pfunc.h>
     11#include "../aoa.h"
     12
     13#define PMF_GPIO(name, bit)					\
     14static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
     15{								\
     16	struct pmf_args args = { .count = 1, .u[0].v = !on };	\
     17	int rc;							\
     18							\
     19	if (unlikely(!rt)) return;				\
     20	rc = pmf_call_function(rt->node, #name "-mute", &args);	\
     21	if (rc && rc != -ENODEV)				\
     22		printk(KERN_WARNING "pmf_gpio_set_" #name	\
     23		" failed, rc: %d\n", rc);			\
     24	rt->implementation_private &= ~(1<<bit);		\
     25	rt->implementation_private |= (!!on << bit);		\
     26}								\
     27static int pmf_gpio_get_##name(struct gpio_runtime *rt)		\
     28{								\
     29	if (unlikely(!rt)) return 0;				\
     30	return (rt->implementation_private>>bit)&1;		\
     31}
     32
     33PMF_GPIO(headphone, 0);
     34PMF_GPIO(amp, 1);
     35PMF_GPIO(lineout, 2);
     36
     37static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
     38{
     39	struct pmf_args args = { .count = 1, .u[0].v = !!on };
     40	int rc;
     41
     42	if (unlikely(!rt)) return;
     43	rc = pmf_call_function(rt->node, "hw-reset", &args);
     44	if (rc)
     45		printk(KERN_WARNING "pmf_gpio_set_hw_reset"
     46		       " failed, rc: %d\n", rc);
     47}
     48
     49static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
     50{
     51	int saved;
     52
     53	if (unlikely(!rt)) return;
     54	saved = rt->implementation_private;
     55	pmf_gpio_set_headphone(rt, 0);
     56	pmf_gpio_set_amp(rt, 0);
     57	pmf_gpio_set_lineout(rt, 0);
     58	rt->implementation_private = saved;
     59}
     60
     61static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
     62{
     63	int s;
     64
     65	if (unlikely(!rt)) return;
     66	s = rt->implementation_private;
     67	pmf_gpio_set_headphone(rt, (s>>0)&1);
     68	pmf_gpio_set_amp(rt, (s>>1)&1);
     69	pmf_gpio_set_lineout(rt, (s>>2)&1);
     70}
     71
     72static void pmf_handle_notify(struct work_struct *work)
     73{
     74	struct gpio_notification *notif =
     75		container_of(work, struct gpio_notification, work.work);
     76
     77	mutex_lock(&notif->mutex);
     78	if (notif->notify)
     79		notif->notify(notif->data);
     80	mutex_unlock(&notif->mutex);
     81}
     82
     83static void pmf_gpio_init(struct gpio_runtime *rt)
     84{
     85	pmf_gpio_all_amps_off(rt);
     86	rt->implementation_private = 0;
     87	INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify);
     88	INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify);
     89	INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify);
     90	mutex_init(&rt->headphone_notify.mutex);
     91	mutex_init(&rt->line_in_notify.mutex);
     92	mutex_init(&rt->line_out_notify.mutex);
     93}
     94
     95static void pmf_gpio_exit(struct gpio_runtime *rt)
     96{
     97	pmf_gpio_all_amps_off(rt);
     98	rt->implementation_private = 0;
     99
    100	if (rt->headphone_notify.gpio_private)
    101		pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
    102	if (rt->line_in_notify.gpio_private)
    103		pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
    104	if (rt->line_out_notify.gpio_private)
    105		pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
    106
    107	/* make sure no work is pending before freeing
    108	 * all things */
    109	cancel_delayed_work_sync(&rt->headphone_notify.work);
    110	cancel_delayed_work_sync(&rt->line_in_notify.work);
    111	cancel_delayed_work_sync(&rt->line_out_notify.work);
    112
    113	mutex_destroy(&rt->headphone_notify.mutex);
    114	mutex_destroy(&rt->line_in_notify.mutex);
    115	mutex_destroy(&rt->line_out_notify.mutex);
    116
    117	kfree(rt->headphone_notify.gpio_private);
    118	kfree(rt->line_in_notify.gpio_private);
    119	kfree(rt->line_out_notify.gpio_private);
    120}
    121
    122static void pmf_handle_notify_irq(void *data)
    123{
    124	struct gpio_notification *notif = data;
    125
    126	schedule_delayed_work(&notif->work, 0);
    127}
    128
    129static int pmf_set_notify(struct gpio_runtime *rt,
    130			  enum notify_type type,
    131			  notify_func_t notify,
    132			  void *data)
    133{
    134	struct gpio_notification *notif;
    135	notify_func_t old;
    136	struct pmf_irq_client *irq_client;
    137	char *name;
    138	int err = -EBUSY;
    139
    140	switch (type) {
    141	case AOA_NOTIFY_HEADPHONE:
    142		notif = &rt->headphone_notify;
    143		name = "headphone-detect";
    144		break;
    145	case AOA_NOTIFY_LINE_IN:
    146		notif = &rt->line_in_notify;
    147		name = "linein-detect";
    148		break;
    149	case AOA_NOTIFY_LINE_OUT:
    150		notif = &rt->line_out_notify;
    151		name = "lineout-detect";
    152		break;
    153	default:
    154		return -EINVAL;
    155	}
    156
    157	mutex_lock(&notif->mutex);
    158
    159	old = notif->notify;
    160
    161	if (!old && !notify) {
    162		err = 0;
    163		goto out_unlock;
    164	}
    165
    166	if (old && notify) {
    167		if (old == notify && notif->data == data)
    168			err = 0;
    169		goto out_unlock;
    170	}
    171
    172	if (old && !notify) {
    173		irq_client = notif->gpio_private;
    174		pmf_unregister_irq_client(irq_client);
    175		kfree(irq_client);
    176		notif->gpio_private = NULL;
    177	}
    178	if (!old && notify) {
    179		irq_client = kzalloc(sizeof(struct pmf_irq_client),
    180				     GFP_KERNEL);
    181		if (!irq_client) {
    182			err = -ENOMEM;
    183			goto out_unlock;
    184		}
    185		irq_client->data = notif;
    186		irq_client->handler = pmf_handle_notify_irq;
    187		irq_client->owner = THIS_MODULE;
    188		err = pmf_register_irq_client(rt->node,
    189					      name,
    190					      irq_client);
    191		if (err) {
    192			printk(KERN_ERR "snd-aoa: gpio layer failed to"
    193					" register %s irq (%d)\n", name, err);
    194			kfree(irq_client);
    195			goto out_unlock;
    196		}
    197		notif->gpio_private = irq_client;
    198	}
    199	notif->notify = notify;
    200	notif->data = data;
    201
    202	err = 0;
    203 out_unlock:
    204	mutex_unlock(&notif->mutex);
    205	return err;
    206}
    207
    208static int pmf_get_detect(struct gpio_runtime *rt,
    209			  enum notify_type type)
    210{
    211	char *name;
    212	int err = -EBUSY, ret;
    213	struct pmf_args args = { .count = 1, .u[0].p = &ret };
    214
    215	switch (type) {
    216	case AOA_NOTIFY_HEADPHONE:
    217		name = "headphone-detect";
    218		break;
    219	case AOA_NOTIFY_LINE_IN:
    220		name = "linein-detect";
    221		break;
    222	case AOA_NOTIFY_LINE_OUT:
    223		name = "lineout-detect";
    224		break;
    225	default:
    226		return -EINVAL;
    227	}
    228
    229	err = pmf_call_function(rt->node, name, &args);
    230	if (err)
    231		return err;
    232	return ret;
    233}
    234
    235static struct gpio_methods methods = {
    236	.init			= pmf_gpio_init,
    237	.exit			= pmf_gpio_exit,
    238	.all_amps_off		= pmf_gpio_all_amps_off,
    239	.all_amps_restore	= pmf_gpio_all_amps_restore,
    240	.set_headphone		= pmf_gpio_set_headphone,
    241	.set_speakers		= pmf_gpio_set_amp,
    242	.set_lineout		= pmf_gpio_set_lineout,
    243	.set_hw_reset		= pmf_gpio_set_hw_reset,
    244	.get_headphone		= pmf_gpio_get_headphone,
    245	.get_speakers		= pmf_gpio_get_amp,
    246	.get_lineout		= pmf_gpio_get_lineout,
    247	.set_notify		= pmf_set_notify,
    248	.get_detect		= pmf_get_detect,
    249};
    250
    251struct gpio_methods *pmf_gpio_methods = &methods;
    252EXPORT_SYMBOL_GPL(pmf_gpio_methods);