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

serial_mctrl_gpio.c (7349B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Helpers for controlling modem lines via GPIO
      4 *
      5 * Copyright (C) 2014 Paratronic S.A.
      6 */
      7
      8#include <linux/err.h>
      9#include <linux/device.h>
     10#include <linux/irq.h>
     11#include <linux/gpio/consumer.h>
     12#include <linux/termios.h>
     13#include <linux/serial_core.h>
     14#include <linux/module.h>
     15#include <linux/property.h>
     16
     17#include "serial_mctrl_gpio.h"
     18
     19struct mctrl_gpios {
     20	struct uart_port *port;
     21	struct gpio_desc *gpio[UART_GPIO_MAX];
     22	int irq[UART_GPIO_MAX];
     23	unsigned int mctrl_prev;
     24	bool mctrl_on;
     25};
     26
     27static const struct {
     28	const char *name;
     29	unsigned int mctrl;
     30	enum gpiod_flags flags;
     31} mctrl_gpios_desc[UART_GPIO_MAX] = {
     32	{ "cts", TIOCM_CTS, GPIOD_IN, },
     33	{ "dsr", TIOCM_DSR, GPIOD_IN, },
     34	{ "dcd", TIOCM_CD,  GPIOD_IN, },
     35	{ "rng", TIOCM_RNG, GPIOD_IN, },
     36	{ "rts", TIOCM_RTS, GPIOD_OUT_LOW, },
     37	{ "dtr", TIOCM_DTR, GPIOD_OUT_LOW, },
     38};
     39
     40static bool mctrl_gpio_flags_is_dir_out(unsigned int idx)
     41{
     42	return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT;
     43}
     44
     45void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
     46{
     47	enum mctrl_gpio_idx i;
     48	struct gpio_desc *desc_array[UART_GPIO_MAX];
     49	DECLARE_BITMAP(values, UART_GPIO_MAX);
     50	unsigned int count = 0;
     51
     52	if (gpios == NULL)
     53		return;
     54
     55	for (i = 0; i < UART_GPIO_MAX; i++)
     56		if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
     57			desc_array[count] = gpios->gpio[i];
     58			__assign_bit(count, values,
     59				     mctrl & mctrl_gpios_desc[i].mctrl);
     60			count++;
     61		}
     62	gpiod_set_array_value(count, desc_array, NULL, values);
     63}
     64EXPORT_SYMBOL_GPL(mctrl_gpio_set);
     65
     66struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
     67				      enum mctrl_gpio_idx gidx)
     68{
     69	if (gpios == NULL)
     70		return NULL;
     71
     72	return gpios->gpio[gidx];
     73}
     74EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
     75
     76unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
     77{
     78	enum mctrl_gpio_idx i;
     79
     80	if (gpios == NULL)
     81		return *mctrl;
     82
     83	for (i = 0; i < UART_GPIO_MAX; i++) {
     84		if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) {
     85			if (gpiod_get_value(gpios->gpio[i]))
     86				*mctrl |= mctrl_gpios_desc[i].mctrl;
     87			else
     88				*mctrl &= ~mctrl_gpios_desc[i].mctrl;
     89		}
     90	}
     91
     92	return *mctrl;
     93}
     94EXPORT_SYMBOL_GPL(mctrl_gpio_get);
     95
     96unsigned int
     97mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl)
     98{
     99	enum mctrl_gpio_idx i;
    100
    101	if (gpios == NULL)
    102		return *mctrl;
    103
    104	for (i = 0; i < UART_GPIO_MAX; i++) {
    105		if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
    106			if (gpiod_get_value(gpios->gpio[i]))
    107				*mctrl |= mctrl_gpios_desc[i].mctrl;
    108			else
    109				*mctrl &= ~mctrl_gpios_desc[i].mctrl;
    110		}
    111	}
    112
    113	return *mctrl;
    114}
    115EXPORT_SYMBOL_GPL(mctrl_gpio_get_outputs);
    116
    117struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
    118{
    119	struct mctrl_gpios *gpios;
    120	enum mctrl_gpio_idx i;
    121
    122	gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
    123	if (!gpios)
    124		return ERR_PTR(-ENOMEM);
    125
    126	for (i = 0; i < UART_GPIO_MAX; i++) {
    127		char *gpio_str;
    128		bool present;
    129
    130		/* Check if GPIO property exists and continue if not */
    131		gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
    132				     mctrl_gpios_desc[i].name);
    133		if (!gpio_str)
    134			continue;
    135
    136		present = device_property_present(dev, gpio_str);
    137		kfree(gpio_str);
    138		if (!present)
    139			continue;
    140
    141		gpios->gpio[i] =
    142			devm_gpiod_get_index_optional(dev,
    143						      mctrl_gpios_desc[i].name,
    144						      idx,
    145						      mctrl_gpios_desc[i].flags);
    146
    147		if (IS_ERR(gpios->gpio[i]))
    148			return ERR_CAST(gpios->gpio[i]);
    149	}
    150
    151	return gpios;
    152}
    153EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto);
    154
    155#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
    156static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
    157{
    158	struct mctrl_gpios *gpios = context;
    159	struct uart_port *port = gpios->port;
    160	u32 mctrl = gpios->mctrl_prev;
    161	u32 mctrl_diff;
    162	unsigned long flags;
    163
    164	mctrl_gpio_get(gpios, &mctrl);
    165
    166	spin_lock_irqsave(&port->lock, flags);
    167
    168	mctrl_diff = mctrl ^ gpios->mctrl_prev;
    169	gpios->mctrl_prev = mctrl;
    170
    171	if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) {
    172		if ((mctrl_diff & mctrl) & TIOCM_RI)
    173			port->icount.rng++;
    174
    175		if ((mctrl_diff & mctrl) & TIOCM_DSR)
    176			port->icount.dsr++;
    177
    178		if (mctrl_diff & TIOCM_CD)
    179			uart_handle_dcd_change(port, mctrl & TIOCM_CD);
    180
    181		if (mctrl_diff & TIOCM_CTS)
    182			uart_handle_cts_change(port, mctrl & TIOCM_CTS);
    183
    184		wake_up_interruptible(&port->state->port.delta_msr_wait);
    185	}
    186
    187	spin_unlock_irqrestore(&port->lock, flags);
    188
    189	return IRQ_HANDLED;
    190}
    191
    192struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
    193{
    194	struct mctrl_gpios *gpios;
    195	enum mctrl_gpio_idx i;
    196
    197	gpios = mctrl_gpio_init_noauto(port->dev, idx);
    198	if (IS_ERR(gpios))
    199		return gpios;
    200
    201	gpios->port = port;
    202
    203	for (i = 0; i < UART_GPIO_MAX; ++i) {
    204		int ret;
    205
    206		if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i))
    207			continue;
    208
    209		ret = gpiod_to_irq(gpios->gpio[i]);
    210		if (ret < 0) {
    211			dev_err(port->dev,
    212				"failed to find corresponding irq for %s (idx=%d, err=%d)\n",
    213				mctrl_gpios_desc[i].name, idx, ret);
    214			return ERR_PTR(ret);
    215		}
    216		gpios->irq[i] = ret;
    217
    218		/* irqs should only be enabled in .enable_ms */
    219		irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN);
    220
    221		ret = devm_request_irq(port->dev, gpios->irq[i],
    222				       mctrl_gpio_irq_handle,
    223				       IRQ_TYPE_EDGE_BOTH, dev_name(port->dev),
    224				       gpios);
    225		if (ret) {
    226			/* alternatively implement polling */
    227			dev_err(port->dev,
    228				"failed to request irq for %s (idx=%d, err=%d)\n",
    229				mctrl_gpios_desc[i].name, idx, ret);
    230			return ERR_PTR(ret);
    231		}
    232	}
    233
    234	return gpios;
    235}
    236EXPORT_SYMBOL_GPL(mctrl_gpio_init);
    237
    238void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
    239{
    240	enum mctrl_gpio_idx i;
    241
    242	if (gpios == NULL)
    243		return;
    244
    245	for (i = 0; i < UART_GPIO_MAX; i++) {
    246		if (gpios->irq[i])
    247			devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
    248
    249		if (gpios->gpio[i])
    250			devm_gpiod_put(dev, gpios->gpio[i]);
    251	}
    252	devm_kfree(dev, gpios);
    253}
    254EXPORT_SYMBOL_GPL(mctrl_gpio_free);
    255
    256void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
    257{
    258	enum mctrl_gpio_idx i;
    259
    260	if (gpios == NULL)
    261		return;
    262
    263	/* .enable_ms may be called multiple times */
    264	if (gpios->mctrl_on)
    265		return;
    266
    267	gpios->mctrl_on = true;
    268
    269	/* get initial status of modem lines GPIOs */
    270	mctrl_gpio_get(gpios, &gpios->mctrl_prev);
    271
    272	for (i = 0; i < UART_GPIO_MAX; ++i) {
    273		if (!gpios->irq[i])
    274			continue;
    275
    276		enable_irq(gpios->irq[i]);
    277	}
    278}
    279EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
    280
    281void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
    282{
    283	enum mctrl_gpio_idx i;
    284
    285	if (gpios == NULL)
    286		return;
    287
    288	if (!gpios->mctrl_on)
    289		return;
    290
    291	gpios->mctrl_on = false;
    292
    293	for (i = 0; i < UART_GPIO_MAX; ++i) {
    294		if (!gpios->irq[i])
    295			continue;
    296
    297		disable_irq(gpios->irq[i]);
    298	}
    299}
    300EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
    301
    302void mctrl_gpio_enable_irq_wake(struct mctrl_gpios *gpios)
    303{
    304	enum mctrl_gpio_idx i;
    305
    306	if (!gpios)
    307		return;
    308
    309	if (!gpios->mctrl_on)
    310		return;
    311
    312	for (i = 0; i < UART_GPIO_MAX; ++i) {
    313		if (!gpios->irq[i])
    314			continue;
    315
    316		enable_irq_wake(gpios->irq[i]);
    317	}
    318}
    319EXPORT_SYMBOL_GPL(mctrl_gpio_enable_irq_wake);
    320
    321void mctrl_gpio_disable_irq_wake(struct mctrl_gpios *gpios)
    322{
    323	enum mctrl_gpio_idx i;
    324
    325	if (!gpios)
    326		return;
    327
    328	if (!gpios->mctrl_on)
    329		return;
    330
    331	for (i = 0; i < UART_GPIO_MAX; ++i) {
    332		if (!gpios->irq[i])
    333			continue;
    334
    335		disable_irq_wake(gpios->irq[i]);
    336	}
    337}
    338EXPORT_SYMBOL_GPL(mctrl_gpio_disable_irq_wake);
    339
    340MODULE_LICENSE("GPL");