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

mod.c (8319B)


      1// SPDX-License-Identifier: GPL-1.0+
      2/*
      3 * Renesas USB driver
      4 *
      5 * Copyright (C) 2011 Renesas Solutions Corp.
      6 * Copyright (C) 2019 Renesas Electronics Corporation
      7 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
      8 */
      9#include <linux/interrupt.h>
     10
     11#include "common.h"
     12#include "mod.h"
     13
     14/*
     15 *		autonomy
     16 *
     17 * these functions are used if platform doesn't have external phy.
     18 *  -> there is no "notify_hotplug" callback from platform
     19 *  -> call "notify_hotplug" by itself
     20 *  -> use own interrupt to connect/disconnect
     21 *  -> it mean module clock is always ON
     22 *             ~~~~~~~~~~~~~~~~~~~~~~~~~
     23 */
     24static int usbhsm_autonomy_get_vbus(struct platform_device *pdev)
     25{
     26	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
     27
     28	return  VBSTS & usbhs_read(priv, INTSTS0);
     29}
     30
     31static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv,
     32				    struct usbhs_irq_state *irq_state)
     33{
     34	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
     35
     36	usbhsc_schedule_notify_hotplug(pdev);
     37
     38	return 0;
     39}
     40
     41void usbhs_mod_autonomy_mode(struct usbhs_priv *priv)
     42{
     43	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
     44
     45	info->irq_vbus = usbhsm_autonomy_irq_vbus;
     46	info->get_vbus = usbhsm_autonomy_get_vbus;
     47
     48	usbhs_irq_callback_update(priv, NULL);
     49}
     50
     51void usbhs_mod_non_autonomy_mode(struct usbhs_priv *priv)
     52{
     53	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
     54
     55	info->get_vbus = priv->pfunc->get_vbus;
     56}
     57
     58/*
     59 *		host / gadget functions
     60 *
     61 * renesas_usbhs host/gadget can register itself by below functions.
     62 * these functions are called when probe
     63 *
     64 */
     65void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
     66{
     67	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
     68
     69	info->mod[id]	= mod;
     70	mod->priv	= priv;
     71}
     72
     73struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
     74{
     75	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
     76	struct usbhs_mod *ret = NULL;
     77
     78	switch (id) {
     79	case USBHS_HOST:
     80	case USBHS_GADGET:
     81		ret = info->mod[id];
     82		break;
     83	}
     84
     85	return ret;
     86}
     87
     88int usbhs_mod_is_host(struct usbhs_priv *priv)
     89{
     90	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
     91	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
     92
     93	if (!mod)
     94		return -EINVAL;
     95
     96	return info->mod[USBHS_HOST] == mod;
     97}
     98
     99struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
    100{
    101	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
    102
    103	return info->curt;
    104}
    105
    106int usbhs_mod_change(struct usbhs_priv *priv, int id)
    107{
    108	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
    109	struct usbhs_mod *mod = NULL;
    110	int ret = 0;
    111
    112	/* id < 0 mean no current */
    113	switch (id) {
    114	case USBHS_HOST:
    115	case USBHS_GADGET:
    116		mod = info->mod[id];
    117		break;
    118	default:
    119		ret = -EINVAL;
    120	}
    121	info->curt = mod;
    122
    123	return ret;
    124}
    125
    126static irqreturn_t usbhs_interrupt(int irq, void *data);
    127int usbhs_mod_probe(struct usbhs_priv *priv)
    128{
    129	struct device *dev = usbhs_priv_to_dev(priv);
    130	int ret;
    131
    132	/*
    133	 * install host/gadget driver
    134	 */
    135	ret = usbhs_mod_host_probe(priv);
    136	if (ret < 0)
    137		return ret;
    138
    139	ret = usbhs_mod_gadget_probe(priv);
    140	if (ret < 0)
    141		goto mod_init_host_err;
    142
    143	/* irq settings */
    144	ret = devm_request_irq(dev, priv->irq, usbhs_interrupt,
    145			       0, dev_name(dev), priv);
    146	if (ret) {
    147		dev_err(dev, "irq request err\n");
    148		goto mod_init_gadget_err;
    149	}
    150
    151	return ret;
    152
    153mod_init_gadget_err:
    154	usbhs_mod_gadget_remove(priv);
    155mod_init_host_err:
    156	usbhs_mod_host_remove(priv);
    157
    158	return ret;
    159}
    160
    161void usbhs_mod_remove(struct usbhs_priv *priv)
    162{
    163	usbhs_mod_host_remove(priv);
    164	usbhs_mod_gadget_remove(priv);
    165}
    166
    167/*
    168 *		status functions
    169 */
    170int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
    171{
    172	return (int)irq_state->intsts0 & DVSQ_MASK;
    173}
    174
    175int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
    176{
    177	/*
    178	 * return value
    179	 *
    180	 * IDLE_SETUP_STAGE
    181	 * READ_DATA_STAGE
    182	 * READ_STATUS_STAGE
    183	 * WRITE_DATA_STAGE
    184	 * WRITE_STATUS_STAGE
    185	 * NODATA_STATUS_STAGE
    186	 * SEQUENCE_ERROR
    187	 */
    188	return (int)irq_state->intsts0 & CTSQ_MASK;
    189}
    190
    191static int usbhs_status_get_each_irq(struct usbhs_priv *priv,
    192				     struct usbhs_irq_state *state)
    193{
    194	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
    195	u16 intenb0, intenb1;
    196	unsigned long flags;
    197
    198	/********************  spin lock ********************/
    199	usbhs_lock(priv, flags);
    200	state->intsts0 = usbhs_read(priv, INTSTS0);
    201	intenb0 = usbhs_read(priv, INTENB0);
    202
    203	if (usbhs_mod_is_host(priv)) {
    204		state->intsts1 = usbhs_read(priv, INTSTS1);
    205		intenb1 = usbhs_read(priv, INTENB1);
    206	} else {
    207		state->intsts1 = intenb1 = 0;
    208	}
    209
    210	/* mask */
    211	if (mod) {
    212		state->brdysts = usbhs_read(priv, BRDYSTS);
    213		state->nrdysts = usbhs_read(priv, NRDYSTS);
    214		state->bempsts = usbhs_read(priv, BEMPSTS);
    215
    216		state->bempsts &= mod->irq_bempsts;
    217		state->brdysts &= mod->irq_brdysts;
    218	}
    219	usbhs_unlock(priv, flags);
    220	/********************  spin unlock ******************/
    221
    222	return 0;
    223}
    224
    225/*
    226 *		interrupt
    227 */
    228#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
    229#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
    230static irqreturn_t usbhs_interrupt(int irq, void *data)
    231{
    232	struct usbhs_priv *priv = data;
    233	struct usbhs_irq_state irq_state;
    234
    235	if (usbhs_status_get_each_irq(priv, &irq_state) < 0)
    236		return IRQ_NONE;
    237
    238	/*
    239	 * clear interrupt
    240	 *
    241	 * The hardware is _very_ picky to clear interrupt bit.
    242	 * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value.
    243	 *
    244	 * see
    245	 *	"Operation"
    246	 *	 - "Control Transfer (DCP)"
    247	 *	   - Function :: VALID bit should 0
    248	 */
    249	usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
    250	if (usbhs_mod_is_host(priv))
    251		usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
    252
    253	/*
    254	 * The driver should not clear the xxxSTS after the line of
    255	 * "call irq callback functions" because each "if" statement is
    256	 * possible to call the callback function for avoiding any side effects.
    257	 */
    258	if (irq_state.intsts0 & BRDY)
    259		usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
    260	usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
    261	if (irq_state.intsts0 & BEMP)
    262		usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
    263
    264	/*
    265	 * call irq callback functions
    266	 * see also
    267	 *	usbhs_irq_setting_update
    268	 */
    269
    270	/* INTSTS0 */
    271	if (irq_state.intsts0 & VBINT)
    272		usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state);
    273
    274	if (irq_state.intsts0 & DVST)
    275		usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
    276
    277	if (irq_state.intsts0 & CTRT)
    278		usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state);
    279
    280	if (irq_state.intsts0 & BEMP)
    281		usbhs_mod_call(priv, irq_empty, priv, &irq_state);
    282
    283	if (irq_state.intsts0 & BRDY)
    284		usbhs_mod_call(priv, irq_ready, priv, &irq_state);
    285
    286	if (usbhs_mod_is_host(priv)) {
    287		/* INTSTS1 */
    288		if (irq_state.intsts1 & ATTCH)
    289			usbhs_mod_call(priv, irq_attch, priv, &irq_state);
    290
    291		if (irq_state.intsts1 & DTCH)
    292			usbhs_mod_call(priv, irq_dtch, priv, &irq_state);
    293
    294		if (irq_state.intsts1 & SIGN)
    295			usbhs_mod_call(priv, irq_sign, priv, &irq_state);
    296
    297		if (irq_state.intsts1 & SACK)
    298			usbhs_mod_call(priv, irq_sack, priv, &irq_state);
    299	}
    300	return IRQ_HANDLED;
    301}
    302
    303void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
    304{
    305	u16 intenb0 = 0;
    306	u16 intenb1 = 0;
    307	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
    308
    309	/*
    310	 * BEMPENB/BRDYENB are picky.
    311	 * below method is required
    312	 *
    313	 *  - clear  INTSTS0
    314	 *  - update BEMPENB/BRDYENB
    315	 *  - update INTSTS0
    316	 */
    317	usbhs_write(priv, INTENB0, 0);
    318	if (usbhs_mod_is_host(priv))
    319		usbhs_write(priv, INTENB1, 0);
    320
    321	usbhs_write(priv, BEMPENB, 0);
    322	usbhs_write(priv, BRDYENB, 0);
    323
    324	/*
    325	 * see also
    326	 *	usbhs_interrupt
    327	 */
    328
    329	if (info->irq_vbus)
    330		intenb0 |= VBSE;
    331
    332	if (mod) {
    333		/*
    334		 * INTSTS0
    335		 */
    336		if (mod->irq_ctrl_stage)
    337			intenb0 |= CTRE;
    338
    339		if (mod->irq_dev_state)
    340			intenb0 |= DVSE;
    341
    342		if (mod->irq_empty && mod->irq_bempsts) {
    343			usbhs_write(priv, BEMPENB, mod->irq_bempsts);
    344			intenb0 |= BEMPE;
    345		}
    346
    347		if (mod->irq_ready && mod->irq_brdysts) {
    348			usbhs_write(priv, BRDYENB, mod->irq_brdysts);
    349			intenb0 |= BRDYE;
    350		}
    351
    352		if (usbhs_mod_is_host(priv)) {
    353			/*
    354			 * INTSTS1
    355			 */
    356			if (mod->irq_attch)
    357				intenb1 |= ATTCHE;
    358
    359			if (mod->irq_dtch)
    360				intenb1 |= DTCHE;
    361
    362			if (mod->irq_sign)
    363				intenb1 |= SIGNE;
    364
    365			if (mod->irq_sack)
    366				intenb1 |= SACKE;
    367		}
    368	}
    369
    370	if (intenb0)
    371		usbhs_write(priv, INTENB0, intenb0);
    372
    373	if (usbhs_mod_is_host(priv) && intenb1)
    374		usbhs_write(priv, INTENB1, intenb1);
    375}