mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2025-11-21 21:20:07 +00:00
At this time, it's an interesting mixture of changes for both old and
new stuff. Majority of changes are about ASoC (lots of systematic
changes for converting remove callbacks to void, and cleanups), while
we got the fixes and the enhancements of very old PCI cards, too.
Here are some highlights:
ALSA/ASoC Core:
- Continued effort of more ASoC core cleanups
- Minor improvements for XRUN handling in indirect PCM helpers
- Code refactoring of PCM core code
ASoC:
- Continued feature and simplification work on SOF, including addition
of a no-DSP mode for bringup, HDA MLink and extensions to the IPC4
protocol
- Hibernation support for CS35L45
- More DT binding conversions
- Support for Cirrus Logic CS35L56, Freescale QMC, Maxim MAX98363,
nVidia systems with MAX9809x and RT5631, Realtek RT712, Renesas R-Car
Gen4, Rockchip RK3588 and TI TAS5733
ALSA:
- Lots of works for legacy emu10k1 and ymfpci PCI drivers
- PCM kselftest fixes and enhancements
-----BEGIN PGP SIGNATURE-----
iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmRJBkcOHHRpd2FpQHN1
c2UuZGUACgkQLtJE4w1nLE8S/Q/+If1MEW+XXYushYU6VcWbHevwsRwmUZPtIJzT
Nx4PE4Ia8rX++GbsH5Iqt6tmldbb/vMbwy7TGbn/Q4ju2cO5qGT4/qgWdC2TuUX6
icWRHslJ//TffSd/yh1g6JIKBlcCmQeYcw5KoaLzBE/qO3iRP0IQUc17gkLKYNni
u1XOGrU9zuh3uwz+UQFfUhB8NlKhD3HVYjwrbd3gwcDsE/0G+q76A/wWghfA+RAb
0ruDhIDtJoem6PKQTwC05UgDpmwd7XFAIgcbOu7E7t/lr4YKwQZhQmJI0IexCR9i
aLPqg3Q/6S+WFKpcPcGCHNljqRNp9lUlIXak+NsbCZ7mXKE6tALywAtuB57sZ0sO
QM1YrmUAsi0RaD7foPcT64CAq8IVQ6aLWusXwvcxzzvJuHvJdeiBKiI5gmF0GqMu
ZLpAMGCoKxft4Il2r+BPTbLHe57uHmp1fKMWUK4NfyIUW7jEdKmf7ALSSJmvcqwU
+R0PXikc0lOo1GH9ZQojpVNFwV8XLOd2CWaNfoPl85A0+ngYhTY3ZRQ3qbYWHlU6
zXAu06IUOef5phsn3zerJ1orV729Xdjf+JUbL0uxJvANsX6R93CQWw0tgrUI62EZ
0vhoOp3PPZUKmDKvUo/NtIyuvSGREg3wDug5tiDOb53Qwfr2VIThJa999kNzH76c
lHUfrv4=
=7XGG
-----END PGP SIGNATURE-----
Merge tag 'sound-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"At this time, it's an interesting mixture of changes for both old and
new stuff. Majority of changes are about ASoC (lots of systematic
changes for converting remove callbacks to void, and cleanups), while
we got the fixes and the enhancements of very old PCI cards, too.
Here are some highlights:
ALSA/ASoC Core:
- Continued effort of more ASoC core cleanups
- Minor improvements for XRUN handling in indirect PCM helpers
- Code refactoring of PCM core code
ASoC:
- Continued feature and simplification work on SOF, including
addition of a no-DSP mode for bringup, HDA MLink and extensions to
the IPC4 protocol
- Hibernation support for CS35L45
- More DT binding conversions
- Support for Cirrus Logic CS35L56, Freescale QMC, Maxim MAX98363,
nVidia systems with MAX9809x and RT5631, Realtek RT712, Renesas
R-Car Gen4, Rockchip RK3588 and TI TAS5733
ALSA:
- Lots of works for legacy emu10k1 and ymfpci PCI drivers
- PCM kselftest fixes and enhancements"
* tag 'sound-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (586 commits)
ALSA: emu10k1: use high-level I/O in set_filterQ()
ALSA: emu10k1: use high-level I/O functions also during init
ALSA: emu10k1: fix error handling in snd_audigy_i2c_volume_put()
ALSA: emu10k1: don't stop DSP in _snd_emu10k1_{,audigy_}init_efx()
ALSA: emu10k1: fix SNDRV_EMU10K1_IOCTL_SINGLE_STEP
ALSA: emu10k1: skip Sound Blaster-specific hacks for E-MU cards
ALSA: emu10k1: fixup DSP defines
ALSA: emu10k1: pull in some register definitions from kX-project
ALSA: emu10k1: remove some bogus defines
ALSA: emu10k1: eliminate some unused defines
ALSA: emu10k1: fix lineup of EMU_HANA_* defines
ALSA: emu10k1: comment updates
ALSA: emu10k1: fix snd_emu1010_fpga_read() input masking for rev2 cards
ALSA: emu10k1: remove unused emu->pcm_playback_efx_substream field
ALSA: emu10k1: remove unused `resume` parameter from snd_emu10k1_init()
ALSA: emu10k1: minor optimizations
ALSA: emu10k1: remove remaining cruft from snd_emu10k1_emu1010_init()
ALSA: emu10k1: remove apparently pointless EMU_HANA_OPTION_CARDS reads
ALSA: emu10k1: remove apparently pointless FPGA reads
ALSA: emu10k1: stop doing weird things with HCFG in snd_emu10k1_emu1010_init()
...
641 lines
15 KiB
C
641 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* General Purpose functions for the global management of the
|
|
* Communication Processor Module.
|
|
* Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
|
|
*
|
|
* In addition to the individual control of the communication
|
|
* channels, there are a few functions that globally affect the
|
|
* communication processor.
|
|
*
|
|
* Buffer descriptors must be allocated from the dual ported memory
|
|
* space. The allocator for that is here. When the communication
|
|
* process is reset, we reclaim the memory available. There is
|
|
* currently no deallocator for this memory.
|
|
* The amount of space available is platform dependent. On the
|
|
* MBX, the EPPC software loads additional microcode into the
|
|
* communication processor, and uses some of the DP ram for this
|
|
* purpose. Current, the first 512 bytes and the last 256 bytes of
|
|
* memory are used. Right now I am conservative and only use the
|
|
* memory that can never be used for microcode. If there are
|
|
* applications that require more DP ram, we can expand the boundaries
|
|
* but then we have to be careful of any downloaded microcode.
|
|
*/
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/param.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of_irq.h>
|
|
#include <asm/page.h>
|
|
#include <asm/8xx_immap.h>
|
|
#include <asm/cpm1.h>
|
|
#include <asm/io.h>
|
|
#include <asm/rheap.h>
|
|
#include <asm/cpm.h>
|
|
|
|
#include <asm/fs_pd.h>
|
|
|
|
#ifdef CONFIG_8xx_GPIO
|
|
#include <linux/gpio/legacy-of-mm-gpiochip.h>
|
|
#endif
|
|
|
|
#define CPM_MAP_SIZE (0x4000)
|
|
|
|
cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */
|
|
immap_t __iomem *mpc8xx_immr = (void __iomem *)VIRT_IMMR_BASE;
|
|
|
|
void __init cpm_reset(void)
|
|
{
|
|
sysconf8xx_t __iomem *siu_conf;
|
|
|
|
cpmp = &mpc8xx_immr->im_cpm;
|
|
|
|
#ifndef CONFIG_PPC_EARLY_DEBUG_CPM
|
|
/* Perform a reset. */
|
|
out_be16(&cpmp->cp_cpcr, CPM_CR_RST | CPM_CR_FLG);
|
|
|
|
/* Wait for it. */
|
|
while (in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG);
|
|
#endif
|
|
|
|
#ifdef CONFIG_UCODE_PATCH
|
|
cpm_load_patch(cpmp);
|
|
#endif
|
|
|
|
/*
|
|
* Set SDMA Bus Request priority 5.
|
|
* On 860T, this also enables FEC priority 6. I am not sure
|
|
* this is what we really want for some applications, but the
|
|
* manual recommends it.
|
|
* Bit 25, FAM can also be set to use FEC aggressive mode (860T).
|
|
*/
|
|
siu_conf = immr_map(im_siu_conf);
|
|
if ((mfspr(SPRN_IMMR) & 0xffff) == 0x0900) /* MPC885 */
|
|
out_be32(&siu_conf->sc_sdcr, 0x40);
|
|
else
|
|
out_be32(&siu_conf->sc_sdcr, 1);
|
|
immr_unmap(siu_conf);
|
|
}
|
|
|
|
static DEFINE_SPINLOCK(cmd_lock);
|
|
|
|
#define MAX_CR_CMD_LOOPS 10000
|
|
|
|
int cpm_command(u32 command, u8 opcode)
|
|
{
|
|
int i, ret;
|
|
unsigned long flags;
|
|
|
|
if (command & 0xffffff03)
|
|
return -EINVAL;
|
|
|
|
spin_lock_irqsave(&cmd_lock, flags);
|
|
|
|
ret = 0;
|
|
out_be16(&cpmp->cp_cpcr, command | CPM_CR_FLG | (opcode << 8));
|
|
for (i = 0; i < MAX_CR_CMD_LOOPS; i++)
|
|
if ((in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0)
|
|
goto out;
|
|
|
|
printk(KERN_ERR "%s(): Not able to issue CPM command\n", __func__);
|
|
ret = -EIO;
|
|
out:
|
|
spin_unlock_irqrestore(&cmd_lock, flags);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(cpm_command);
|
|
|
|
/*
|
|
* Set a baud rate generator. This needs lots of work. There are
|
|
* four BRGs, any of which can be wired to any channel.
|
|
* The internal baud rate clock is the system clock divided by 16.
|
|
* This assumes the baudrate is 16x oversampled by the uart.
|
|
*/
|
|
#define BRG_INT_CLK (get_brgfreq())
|
|
#define BRG_UART_CLK (BRG_INT_CLK/16)
|
|
#define BRG_UART_CLK_DIV16 (BRG_UART_CLK/16)
|
|
|
|
void
|
|
cpm_setbrg(uint brg, uint rate)
|
|
{
|
|
u32 __iomem *bp;
|
|
|
|
/* This is good enough to get SMCs running..... */
|
|
bp = &cpmp->cp_brgc1;
|
|
bp += brg;
|
|
/*
|
|
* The BRG has a 12-bit counter. For really slow baud rates (or
|
|
* really fast processors), we may have to further divide by 16.
|
|
*/
|
|
if (((BRG_UART_CLK / rate) - 1) < 4096)
|
|
out_be32(bp, (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN);
|
|
else
|
|
out_be32(bp, (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) |
|
|
CPM_BRG_EN | CPM_BRG_DIV16);
|
|
}
|
|
EXPORT_SYMBOL(cpm_setbrg);
|
|
|
|
struct cpm_ioport16 {
|
|
__be16 dir, par, odr_sor, dat, intr;
|
|
__be16 res[3];
|
|
};
|
|
|
|
struct cpm_ioport32b {
|
|
__be32 dir, par, odr, dat;
|
|
};
|
|
|
|
struct cpm_ioport32e {
|
|
__be32 dir, par, sor, odr, dat;
|
|
};
|
|
|
|
static void __init cpm1_set_pin32(int port, int pin, int flags)
|
|
{
|
|
struct cpm_ioport32e __iomem *iop;
|
|
pin = 1 << (31 - pin);
|
|
|
|
if (port == CPM_PORTB)
|
|
iop = (struct cpm_ioport32e __iomem *)
|
|
&mpc8xx_immr->im_cpm.cp_pbdir;
|
|
else
|
|
iop = (struct cpm_ioport32e __iomem *)
|
|
&mpc8xx_immr->im_cpm.cp_pedir;
|
|
|
|
if (flags & CPM_PIN_OUTPUT)
|
|
setbits32(&iop->dir, pin);
|
|
else
|
|
clrbits32(&iop->dir, pin);
|
|
|
|
if (!(flags & CPM_PIN_GPIO))
|
|
setbits32(&iop->par, pin);
|
|
else
|
|
clrbits32(&iop->par, pin);
|
|
|
|
if (port == CPM_PORTB) {
|
|
if (flags & CPM_PIN_OPENDRAIN)
|
|
setbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin);
|
|
else
|
|
clrbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin);
|
|
}
|
|
|
|
if (port == CPM_PORTE) {
|
|
if (flags & CPM_PIN_SECONDARY)
|
|
setbits32(&iop->sor, pin);
|
|
else
|
|
clrbits32(&iop->sor, pin);
|
|
|
|
if (flags & CPM_PIN_OPENDRAIN)
|
|
setbits32(&mpc8xx_immr->im_cpm.cp_peodr, pin);
|
|
else
|
|
clrbits32(&mpc8xx_immr->im_cpm.cp_peodr, pin);
|
|
}
|
|
}
|
|
|
|
static void __init cpm1_set_pin16(int port, int pin, int flags)
|
|
{
|
|
struct cpm_ioport16 __iomem *iop =
|
|
(struct cpm_ioport16 __iomem *)&mpc8xx_immr->im_ioport;
|
|
|
|
pin = 1 << (15 - pin);
|
|
|
|
if (port != 0)
|
|
iop += port - 1;
|
|
|
|
if (flags & CPM_PIN_OUTPUT)
|
|
setbits16(&iop->dir, pin);
|
|
else
|
|
clrbits16(&iop->dir, pin);
|
|
|
|
if (!(flags & CPM_PIN_GPIO))
|
|
setbits16(&iop->par, pin);
|
|
else
|
|
clrbits16(&iop->par, pin);
|
|
|
|
if (port == CPM_PORTA) {
|
|
if (flags & CPM_PIN_OPENDRAIN)
|
|
setbits16(&iop->odr_sor, pin);
|
|
else
|
|
clrbits16(&iop->odr_sor, pin);
|
|
}
|
|
if (port == CPM_PORTC) {
|
|
if (flags & CPM_PIN_SECONDARY)
|
|
setbits16(&iop->odr_sor, pin);
|
|
else
|
|
clrbits16(&iop->odr_sor, pin);
|
|
if (flags & CPM_PIN_FALLEDGE)
|
|
setbits16(&iop->intr, pin);
|
|
else
|
|
clrbits16(&iop->intr, pin);
|
|
}
|
|
}
|
|
|
|
void __init cpm1_set_pin(enum cpm_port port, int pin, int flags)
|
|
{
|
|
if (port == CPM_PORTB || port == CPM_PORTE)
|
|
cpm1_set_pin32(port, pin, flags);
|
|
else
|
|
cpm1_set_pin16(port, pin, flags);
|
|
}
|
|
|
|
int __init cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode)
|
|
{
|
|
int shift;
|
|
int i, bits = 0;
|
|
u32 __iomem *reg;
|
|
u32 mask = 7;
|
|
|
|
u8 clk_map[][3] = {
|
|
{CPM_CLK_SCC1, CPM_BRG1, 0},
|
|
{CPM_CLK_SCC1, CPM_BRG2, 1},
|
|
{CPM_CLK_SCC1, CPM_BRG3, 2},
|
|
{CPM_CLK_SCC1, CPM_BRG4, 3},
|
|
{CPM_CLK_SCC1, CPM_CLK1, 4},
|
|
{CPM_CLK_SCC1, CPM_CLK2, 5},
|
|
{CPM_CLK_SCC1, CPM_CLK3, 6},
|
|
{CPM_CLK_SCC1, CPM_CLK4, 7},
|
|
|
|
{CPM_CLK_SCC2, CPM_BRG1, 0},
|
|
{CPM_CLK_SCC2, CPM_BRG2, 1},
|
|
{CPM_CLK_SCC2, CPM_BRG3, 2},
|
|
{CPM_CLK_SCC2, CPM_BRG4, 3},
|
|
{CPM_CLK_SCC2, CPM_CLK1, 4},
|
|
{CPM_CLK_SCC2, CPM_CLK2, 5},
|
|
{CPM_CLK_SCC2, CPM_CLK3, 6},
|
|
{CPM_CLK_SCC2, CPM_CLK4, 7},
|
|
|
|
{CPM_CLK_SCC3, CPM_BRG1, 0},
|
|
{CPM_CLK_SCC3, CPM_BRG2, 1},
|
|
{CPM_CLK_SCC3, CPM_BRG3, 2},
|
|
{CPM_CLK_SCC3, CPM_BRG4, 3},
|
|
{CPM_CLK_SCC3, CPM_CLK5, 4},
|
|
{CPM_CLK_SCC3, CPM_CLK6, 5},
|
|
{CPM_CLK_SCC3, CPM_CLK7, 6},
|
|
{CPM_CLK_SCC3, CPM_CLK8, 7},
|
|
|
|
{CPM_CLK_SCC4, CPM_BRG1, 0},
|
|
{CPM_CLK_SCC4, CPM_BRG2, 1},
|
|
{CPM_CLK_SCC4, CPM_BRG3, 2},
|
|
{CPM_CLK_SCC4, CPM_BRG4, 3},
|
|
{CPM_CLK_SCC4, CPM_CLK5, 4},
|
|
{CPM_CLK_SCC4, CPM_CLK6, 5},
|
|
{CPM_CLK_SCC4, CPM_CLK7, 6},
|
|
{CPM_CLK_SCC4, CPM_CLK8, 7},
|
|
|
|
{CPM_CLK_SMC1, CPM_BRG1, 0},
|
|
{CPM_CLK_SMC1, CPM_BRG2, 1},
|
|
{CPM_CLK_SMC1, CPM_BRG3, 2},
|
|
{CPM_CLK_SMC1, CPM_BRG4, 3},
|
|
{CPM_CLK_SMC1, CPM_CLK1, 4},
|
|
{CPM_CLK_SMC1, CPM_CLK2, 5},
|
|
{CPM_CLK_SMC1, CPM_CLK3, 6},
|
|
{CPM_CLK_SMC1, CPM_CLK4, 7},
|
|
|
|
{CPM_CLK_SMC2, CPM_BRG1, 0},
|
|
{CPM_CLK_SMC2, CPM_BRG2, 1},
|
|
{CPM_CLK_SMC2, CPM_BRG3, 2},
|
|
{CPM_CLK_SMC2, CPM_BRG4, 3},
|
|
{CPM_CLK_SMC2, CPM_CLK5, 4},
|
|
{CPM_CLK_SMC2, CPM_CLK6, 5},
|
|
{CPM_CLK_SMC2, CPM_CLK7, 6},
|
|
{CPM_CLK_SMC2, CPM_CLK8, 7},
|
|
};
|
|
|
|
switch (target) {
|
|
case CPM_CLK_SCC1:
|
|
reg = &mpc8xx_immr->im_cpm.cp_sicr;
|
|
shift = 0;
|
|
break;
|
|
|
|
case CPM_CLK_SCC2:
|
|
reg = &mpc8xx_immr->im_cpm.cp_sicr;
|
|
shift = 8;
|
|
break;
|
|
|
|
case CPM_CLK_SCC3:
|
|
reg = &mpc8xx_immr->im_cpm.cp_sicr;
|
|
shift = 16;
|
|
break;
|
|
|
|
case CPM_CLK_SCC4:
|
|
reg = &mpc8xx_immr->im_cpm.cp_sicr;
|
|
shift = 24;
|
|
break;
|
|
|
|
case CPM_CLK_SMC1:
|
|
reg = &mpc8xx_immr->im_cpm.cp_simode;
|
|
shift = 12;
|
|
break;
|
|
|
|
case CPM_CLK_SMC2:
|
|
reg = &mpc8xx_immr->im_cpm.cp_simode;
|
|
shift = 28;
|
|
break;
|
|
|
|
default:
|
|
printk(KERN_ERR "cpm1_clock_setup: invalid clock target\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(clk_map); i++) {
|
|
if (clk_map[i][0] == target && clk_map[i][1] == clock) {
|
|
bits = clk_map[i][2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(clk_map)) {
|
|
printk(KERN_ERR "cpm1_clock_setup: invalid clock combination\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bits <<= shift;
|
|
mask <<= shift;
|
|
|
|
if (reg == &mpc8xx_immr->im_cpm.cp_sicr) {
|
|
if (mode == CPM_CLK_RTX) {
|
|
bits |= bits << 3;
|
|
mask |= mask << 3;
|
|
} else if (mode == CPM_CLK_RX) {
|
|
bits <<= 3;
|
|
mask <<= 3;
|
|
}
|
|
}
|
|
|
|
out_be32(reg, (in_be32(reg) & ~mask) | bits);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* GPIO LIB API implementation
|
|
*/
|
|
#ifdef CONFIG_8xx_GPIO
|
|
|
|
struct cpm1_gpio16_chip {
|
|
struct of_mm_gpio_chip mm_gc;
|
|
spinlock_t lock;
|
|
|
|
/* shadowed data register to clear/set bits safely */
|
|
u16 cpdata;
|
|
|
|
/* IRQ associated with Pins when relevant */
|
|
int irq[16];
|
|
};
|
|
|
|
static void cpm1_gpio16_save_regs(struct of_mm_gpio_chip *mm_gc)
|
|
{
|
|
struct cpm1_gpio16_chip *cpm1_gc =
|
|
container_of(mm_gc, struct cpm1_gpio16_chip, mm_gc);
|
|
struct cpm_ioport16 __iomem *iop = mm_gc->regs;
|
|
|
|
cpm1_gc->cpdata = in_be16(&iop->dat);
|
|
}
|
|
|
|
static int cpm1_gpio16_get(struct gpio_chip *gc, unsigned int gpio)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm_ioport16 __iomem *iop = mm_gc->regs;
|
|
u16 pin_mask;
|
|
|
|
pin_mask = 1 << (15 - gpio);
|
|
|
|
return !!(in_be16(&iop->dat) & pin_mask);
|
|
}
|
|
|
|
static void __cpm1_gpio16_set(struct of_mm_gpio_chip *mm_gc, u16 pin_mask,
|
|
int value)
|
|
{
|
|
struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport16 __iomem *iop = mm_gc->regs;
|
|
|
|
if (value)
|
|
cpm1_gc->cpdata |= pin_mask;
|
|
else
|
|
cpm1_gc->cpdata &= ~pin_mask;
|
|
|
|
out_be16(&iop->dat, cpm1_gc->cpdata);
|
|
}
|
|
|
|
static void cpm1_gpio16_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
unsigned long flags;
|
|
u16 pin_mask = 1 << (15 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
__cpm1_gpio16_set(mm_gc, pin_mask, value);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
}
|
|
|
|
static int cpm1_gpio16_to_irq(struct gpio_chip *gc, unsigned int gpio)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
|
|
return cpm1_gc->irq[gpio] ? : -ENXIO;
|
|
}
|
|
|
|
static int cpm1_gpio16_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport16 __iomem *iop = mm_gc->regs;
|
|
unsigned long flags;
|
|
u16 pin_mask = 1 << (15 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
setbits16(&iop->dir, pin_mask);
|
|
__cpm1_gpio16_set(mm_gc, pin_mask, val);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport16 __iomem *iop = mm_gc->regs;
|
|
unsigned long flags;
|
|
u16 pin_mask = 1 << (15 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
clrbits16(&iop->dir, pin_mask);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cpm1_gpiochip_add16(struct device *dev)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
struct cpm1_gpio16_chip *cpm1_gc;
|
|
struct of_mm_gpio_chip *mm_gc;
|
|
struct gpio_chip *gc;
|
|
u16 mask;
|
|
|
|
cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
|
|
if (!cpm1_gc)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&cpm1_gc->lock);
|
|
|
|
if (!of_property_read_u16(np, "fsl,cpm1-gpio-irq-mask", &mask)) {
|
|
int i, j;
|
|
|
|
for (i = 0, j = 0; i < 16; i++)
|
|
if (mask & (1 << (15 - i)))
|
|
cpm1_gc->irq[i] = irq_of_parse_and_map(np, j++);
|
|
}
|
|
|
|
mm_gc = &cpm1_gc->mm_gc;
|
|
gc = &mm_gc->gc;
|
|
|
|
mm_gc->save_regs = cpm1_gpio16_save_regs;
|
|
gc->ngpio = 16;
|
|
gc->direction_input = cpm1_gpio16_dir_in;
|
|
gc->direction_output = cpm1_gpio16_dir_out;
|
|
gc->get = cpm1_gpio16_get;
|
|
gc->set = cpm1_gpio16_set;
|
|
gc->to_irq = cpm1_gpio16_to_irq;
|
|
gc->parent = dev;
|
|
gc->owner = THIS_MODULE;
|
|
|
|
return of_mm_gpiochip_add_data(np, mm_gc, cpm1_gc);
|
|
}
|
|
|
|
struct cpm1_gpio32_chip {
|
|
struct of_mm_gpio_chip mm_gc;
|
|
spinlock_t lock;
|
|
|
|
/* shadowed data register to clear/set bits safely */
|
|
u32 cpdata;
|
|
};
|
|
|
|
static void cpm1_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
|
|
{
|
|
struct cpm1_gpio32_chip *cpm1_gc =
|
|
container_of(mm_gc, struct cpm1_gpio32_chip, mm_gc);
|
|
struct cpm_ioport32b __iomem *iop = mm_gc->regs;
|
|
|
|
cpm1_gc->cpdata = in_be32(&iop->dat);
|
|
}
|
|
|
|
static int cpm1_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm_ioport32b __iomem *iop = mm_gc->regs;
|
|
u32 pin_mask;
|
|
|
|
pin_mask = 1 << (31 - gpio);
|
|
|
|
return !!(in_be32(&iop->dat) & pin_mask);
|
|
}
|
|
|
|
static void __cpm1_gpio32_set(struct of_mm_gpio_chip *mm_gc, u32 pin_mask,
|
|
int value)
|
|
{
|
|
struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport32b __iomem *iop = mm_gc->regs;
|
|
|
|
if (value)
|
|
cpm1_gc->cpdata |= pin_mask;
|
|
else
|
|
cpm1_gc->cpdata &= ~pin_mask;
|
|
|
|
out_be32(&iop->dat, cpm1_gc->cpdata);
|
|
}
|
|
|
|
static void cpm1_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
unsigned long flags;
|
|
u32 pin_mask = 1 << (31 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
__cpm1_gpio32_set(mm_gc, pin_mask, value);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
}
|
|
|
|
static int cpm1_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport32b __iomem *iop = mm_gc->regs;
|
|
unsigned long flags;
|
|
u32 pin_mask = 1 << (31 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
setbits32(&iop->dir, pin_mask);
|
|
__cpm1_gpio32_set(mm_gc, pin_mask, val);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
|
|
{
|
|
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
|
struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
|
|
struct cpm_ioport32b __iomem *iop = mm_gc->regs;
|
|
unsigned long flags;
|
|
u32 pin_mask = 1 << (31 - gpio);
|
|
|
|
spin_lock_irqsave(&cpm1_gc->lock, flags);
|
|
|
|
clrbits32(&iop->dir, pin_mask);
|
|
|
|
spin_unlock_irqrestore(&cpm1_gc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cpm1_gpiochip_add32(struct device *dev)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
struct cpm1_gpio32_chip *cpm1_gc;
|
|
struct of_mm_gpio_chip *mm_gc;
|
|
struct gpio_chip *gc;
|
|
|
|
cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
|
|
if (!cpm1_gc)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&cpm1_gc->lock);
|
|
|
|
mm_gc = &cpm1_gc->mm_gc;
|
|
gc = &mm_gc->gc;
|
|
|
|
mm_gc->save_regs = cpm1_gpio32_save_regs;
|
|
gc->ngpio = 32;
|
|
gc->direction_input = cpm1_gpio32_dir_in;
|
|
gc->direction_output = cpm1_gpio32_dir_out;
|
|
gc->get = cpm1_gpio32_get;
|
|
gc->set = cpm1_gpio32_set;
|
|
gc->parent = dev;
|
|
gc->owner = THIS_MODULE;
|
|
|
|
return of_mm_gpiochip_add_data(np, mm_gc, cpm1_gc);
|
|
}
|
|
|
|
#endif /* CONFIG_8xx_GPIO */
|