Files
linux/drivers/net/wan/framer/pef2256/pef2256.c
Uwe Kleine-König 0d1a7a8fac net: wan: framer/pef2256: Convert to platform remove callback returning void
The .remove() callback for a platform driver returns an int which makes
many driver authors wrongly assume it's possible to do error handling by
returning an error code. However the value returned is ignored (apart
from emitting a warning) and this typically results in resource leaks.

To improve here there is a quest to make the remove callback return
void. In the first step of this quest all drivers are converted to
.remove_new(), which already returns void. Eventually after all drivers
are converted, .remove_new() will be renamed to .remove().

Trivially convert this driver from always returning zero in the remove
callback to the void returning variant.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Acked-by: Herve Codina <herve.codina@bootlin.com>
Link: https://lore.kernel.org/r/9684419fd714cc489a3ef36d838d3717bb6aec6d.1709886922.git.u.kleine-koenig@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-03-11 14:58:20 -07:00

879 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* PEF2256 also known as FALC56 driver
*
* Copyright 2023 CS GROUP France
*
* Author: Herve Codina <herve.codina@bootlin.com>
*/
#include <linux/framer/pef2256.h>
#include <linux/clk.h>
#include <linux/framer/framer-provider.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include "pef2256-regs.h"
enum pef2256_frame_type {
PEF2256_FRAME_E1_DOUBLEFRAME,
PEF2256_FRAME_E1_CRC4_MULTIFRAME,
PEF2256_FRAME_E1_AUTO_MULTIFRAME,
PEF2256_FRAME_T1J1_4FRAME,
PEF2256_FRAME_T1J1_12FRAME,
PEF2256_FRAME_T1J1_24FRAME,
PEF2256_FRAME_T1J1_72FRAME,
};
struct pef2256 {
struct device *dev;
struct regmap *regmap;
enum pef2256_version version;
struct clk *mclk;
struct clk *sclkr;
struct clk *sclkx;
struct gpio_desc *reset_gpio;
unsigned long sysclk_rate;
u32 data_rate;
bool is_tx_falling_edge;
bool is_subordinate;
enum pef2256_frame_type frame_type;
u8 channel_phase;
atomic_t carrier;
struct framer *framer;
};
static u8 pef2256_read8(struct pef2256 *pef2256, int offset)
{
int val;
regmap_read(pef2256->regmap, offset, &val);
return val;
}
static void pef2256_write8(struct pef2256 *pef2256, int offset, u8 val)
{
regmap_write(pef2256->regmap, offset, val);
}
static void pef2256_clrbits8(struct pef2256 *pef2256, int offset, u8 clr)
{
regmap_clear_bits(pef2256->regmap, offset, clr);
}
static void pef2256_setbits8(struct pef2256 *pef2256, int offset, u8 set)
{
regmap_set_bits(pef2256->regmap, offset, set);
}
static void pef2256_clrsetbits8(struct pef2256 *pef2256, int offset, u8 clr, u8 set)
{
regmap_update_bits(pef2256->regmap, offset, clr | set, set);
}
enum pef2256_version pef2256_get_version(struct pef2256 *pef2256)
{
enum pef2256_version version = PEF2256_VERSION_UNKNOWN;
u8 vstr, wid;
vstr = pef2256_read8(pef2256, PEF2256_VSTR);
wid = pef2256_read8(pef2256, PEF2256_WID);
switch (vstr) {
case PEF2256_VSTR_VERSION_12:
if ((wid & PEF2256_12_WID_MASK) == PEF2256_12_WID_VERSION_12)
version = PEF2256_VERSION_1_2;
break;
case PEF2256_VSTR_VERSION_2x:
switch (wid & PEF2256_2X_WID_MASK) {
case PEF2256_2X_WID_VERSION_21:
version = PEF2256_VERSION_2_1;
break;
case PEF2256_2X_WID_VERSION_22:
version = PEF2256_VERSION_2_2;
break;
}
break;
case PEF2256_VSTR_VERSION_21:
version = PEF2256_VERSION_2_1;
break;
}
if (version == PEF2256_VERSION_UNKNOWN)
dev_err(pef2256->dev, "Unknown version (0x%02x, 0x%02x)\n", vstr, wid);
return version;
}
EXPORT_SYMBOL_GPL(pef2256_get_version);
enum pef2256_gcm_config_item {
PEF2256_GCM_CONFIG_1544000 = 0,
PEF2256_GCM_CONFIG_2048000,
PEF2256_GCM_CONFIG_8192000,
PEF2256_GCM_CONFIG_10000000,
PEF2256_GCM_CONFIG_12352000,
PEF2256_GCM_CONFIG_16384000,
};
struct pef2256_gcm_config {
u8 gcm_12[6];
u8 gcm_2x[8];
};
static const struct pef2256_gcm_config pef2256_gcm_configs[] = {
[PEF2256_GCM_CONFIG_1544000] = {
.gcm_12 = {0xF0, 0x51, 0x00, 0x80, 0x00, 0x15},
.gcm_2x = {0x00, 0x15, 0x00, 0x08, 0x00, 0x3F, 0x9C, 0xDF},
},
[PEF2256_GCM_CONFIG_2048000] = {
.gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x00, 0x10},
.gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x00, 0x2F, 0xDB, 0xDF},
},
[PEF2256_GCM_CONFIG_8192000] = {
.gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x03, 0x10},
.gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x00, 0x0B, 0xDB, 0xDF},
},
[PEF2256_GCM_CONFIG_10000000] = {
.gcm_12 = {0x90, 0x51, 0x81, 0x8F, 0x04, 0x10},
.gcm_2x = {0x40, 0x1B, 0x3D, 0x0A, 0x00, 0x07, 0xC9, 0xDC},
},
[PEF2256_GCM_CONFIG_12352000] = {
.gcm_12 = {0xF0, 0x51, 0x00, 0x80, 0x07, 0x15},
.gcm_2x = {0x00, 0x19, 0x00, 0x08, 0x01, 0x0A, 0x98, 0xDA},
},
[PEF2256_GCM_CONFIG_16384000] = {
.gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x07, 0x10},
.gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x01, 0x0B, 0xDB, 0xDF},
},
};
static int pef2256_setup_gcm(struct pef2256 *pef2256)
{
enum pef2256_gcm_config_item item;
unsigned long mclk_rate;
const u8 *gcm;
int i, count;
mclk_rate = clk_get_rate(pef2256->mclk);
switch (mclk_rate) {
case 1544000:
item = PEF2256_GCM_CONFIG_1544000;
break;
case 2048000:
item = PEF2256_GCM_CONFIG_2048000;
break;
case 8192000:
item = PEF2256_GCM_CONFIG_8192000;
break;
case 10000000:
item = PEF2256_GCM_CONFIG_10000000;
break;
case 12352000:
item = PEF2256_GCM_CONFIG_12352000;
break;
case 16384000:
item = PEF2256_GCM_CONFIG_16384000;
break;
default:
dev_err(pef2256->dev, "Unsupported v2.x MCLK rate %lu\n", mclk_rate);
return -EINVAL;
}
BUILD_BUG_ON(item >= ARRAY_SIZE(pef2256_gcm_configs));
if (pef2256->version == PEF2256_VERSION_1_2) {
gcm = pef2256_gcm_configs[item].gcm_12;
count = ARRAY_SIZE(pef2256_gcm_configs[item].gcm_12);
} else {
gcm = pef2256_gcm_configs[item].gcm_2x;
count = ARRAY_SIZE(pef2256_gcm_configs[item].gcm_2x);
}
for (i = 0; i < count; i++)
pef2256_write8(pef2256, PEF2256_GCM(i + 1), *(gcm + i));
return 0;
}
static int pef2256_setup_e1_line(struct pef2256 *pef2256)
{
u8 fmr1, fmr2;
/* RCLK output : DPLL clock, DCO-X enabled, DCO-X internal ref clock */
pef2256_write8(pef2256, PEF2256_CMR1, 0x00);
/* SCLKR selected, SCLKX selected,
* receive synchro pulse sourced by SYPR,
* transmit synchro pulse sourced by SYPX,
* DCO-X center frequency enabled
*/
pef2256_write8(pef2256, PEF2256_CMR2, PEF2256_CMR2_DCOXC);
if (pef2256->is_subordinate) {
/* select RCLK source = 2M, disable switching from RCLK to SYNC */
pef2256_clrsetbits8(pef2256, PEF2256_CMR1, PEF2256_CMR1_RS_MASK,
PEF2256_CMR1_RS_DCOR_2048 | PEF2256_CMR1_DCS);
}
/* slave mode, local loop off, mode short-haul
* In v2.x, bit3 is a forced 1 bit in the datasheet -> Need to be set.
*/
if (pef2256->version == PEF2256_VERSION_1_2)
pef2256_write8(pef2256, PEF2256_LIM0, 0x00);
else
pef2256_write8(pef2256, PEF2256_LIM0, PEF2256_2X_LIM0_BIT3);
/* "master" mode */
if (!pef2256->is_subordinate)
pef2256_setbits8(pef2256, PEF2256_LIM0, PEF2256_LIM0_MAS);
/* analog interface selected, remote loop off */
pef2256_write8(pef2256, PEF2256_LIM1, 0x00);
/* receive input threshold = 0,21V */
if (pef2256->version == PEF2256_VERSION_1_2)
pef2256_clrsetbits8(pef2256, PEF2256_LIM1, PEF2256_12_LIM1_RIL_MASK,
PEF2256_12_LIM1_RIL_210);
else
pef2256_clrsetbits8(pef2256, PEF2256_LIM1, PEF2256_2X_LIM1_RIL_MASK,
PEF2256_2X_LIM1_RIL_210);
/* transmit pulse mask, default value from datasheet
* transmit line in normal operation
*/
if (pef2256->version == PEF2256_VERSION_1_2)
pef2256_write8(pef2256, PEF2256_XPM0, 0x7B);
else
pef2256_write8(pef2256, PEF2256_XPM0, 0x9C);
pef2256_write8(pef2256, PEF2256_XPM1, 0x03);
pef2256_write8(pef2256, PEF2256_XPM2, 0x00);
/* HDB3 coding, no alarm simulation */
pef2256_write8(pef2256, PEF2256_FMR0, PEF2256_FMR0_XC_HDB3 | PEF2256_FMR0_RC_HDB3);
/* E1, frame format, 2 Mbit/s system data rate, no AIS
* transmission to remote end or system interface, payload loop
* off, transmit remote alarm on
*/
fmr1 = 0x00;
fmr2 = PEF2256_FMR2_AXRA;
switch (pef2256->frame_type) {
case PEF2256_FRAME_E1_DOUBLEFRAME:
fmr2 |= PEF2256_FMR2_RFS_DOUBLEFRAME;
break;
case PEF2256_FRAME_E1_CRC4_MULTIFRAME:
fmr1 |= PEF2256_FMR1_XFS;
fmr2 |= PEF2256_FMR2_RFS_CRC4_MULTIFRAME;
break;
case PEF2256_FRAME_E1_AUTO_MULTIFRAME:
fmr1 |= PEF2256_FMR1_XFS;
fmr2 |= PEF2256_FMR2_RFS_AUTO_MULTIFRAME;
break;
default:
dev_err(pef2256->dev, "Unsupported frame type %d\n", pef2256->frame_type);
return -EINVAL;
}
pef2256_clrsetbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_XFS, fmr1);
pef2256_write8(pef2256, PEF2256_FMR2, fmr2);
if (!pef2256->is_subordinate) {
/* SEC input, active high */
pef2256_write8(pef2256, PEF2256_GPC1, PEF2256_GPC1_CSFP_SEC_IN_HIGH);
} else {
/* FSC output, active high */
pef2256_write8(pef2256, PEF2256_GPC1, PEF2256_GPC1_CSFP_FSC_OUT_HIGH);
}
/* SCLKR, SCLKX, RCLK configured to inputs,
* XFMS active low, CLK1 and CLK2 pin configuration
*/
pef2256_write8(pef2256, PEF2256_PC5, 0x00);
pef2256_write8(pef2256, PEF2256_PC6, 0x00);
/* port RCLK is output */
pef2256_setbits8(pef2256, PEF2256_PC5, PEF2256_PC5_CRP);
return 0;
}
static void pef2256_setup_e1_los(struct pef2256 *pef2256)
{
/* detection of LOS alarm = 176 pulses (ie (10 + 1) * 16) */
pef2256_write8(pef2256, PEF2256_PCD, 10);
/* recovery of LOS alarm = 22 pulses (ie 21 + 1) */
pef2256_write8(pef2256, PEF2256_PCR, 21);
/* E1 default for the receive slicer threshold */
pef2256_write8(pef2256, PEF2256_LIM2, PEF2256_LIM2_SLT_THR50);
if (pef2256->is_subordinate) {
/* Loop-timed */
pef2256_setbits8(pef2256, PEF2256_LIM2, PEF2256_LIM2_ELT);
}
}
static int pef2256_setup_e1_system(struct pef2256 *pef2256)
{
u8 sic1, fmr1;
/* 2.048 MHz system clocking rate, receive buffer 2 frames, transmit
* buffer bypass, data sampled and transmitted on the falling edge of
* SCLKR/X, automatic freeze signaling, data is active in the first
* channel phase
*/
pef2256_write8(pef2256, PEF2256_SIC1, 0x00);
pef2256_write8(pef2256, PEF2256_SIC2, 0x00);
pef2256_write8(pef2256, PEF2256_SIC3, 0x00);
if (pef2256->is_subordinate) {
/* transmit buffer size = 2 frames, transparent mode */
pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_XBS_MASK,
PEF2256_SIC1_XBS_2FRAMES);
}
if (pef2256->version != PEF2256_VERSION_1_2) {
/* during inactive channel phase switch RDO/RSIG into tri-state */
pef2256_setbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RTRI);
}
if (pef2256->is_tx_falling_edge) {
/* falling edge sync pulse transmit, rising edge sync pulse receive */
pef2256_clrsetbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RESX, PEF2256_SIC3_RESR);
} else {
/* rising edge sync pulse transmit, falling edge sync pulse receive */
pef2256_clrsetbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RESR, PEF2256_SIC3_RESX);
}
/* transmit offset counter (XCO10..0) = 4 */
pef2256_write8(pef2256, PEF2256_XC0, 0);
pef2256_write8(pef2256, PEF2256_XC1, 4);
/* receive offset counter (RCO10..0) = 4 */
pef2256_write8(pef2256, PEF2256_RC0, 0);
pef2256_write8(pef2256, PEF2256_RC1, 4);
/* system clock rate */
switch (pef2256->sysclk_rate) {
case 2048000:
sic1 = PEF2256_SIC1_SSC_2048;
break;
case 4096000:
sic1 = PEF2256_SIC1_SSC_4096;
break;
case 8192000:
sic1 = PEF2256_SIC1_SSC_8192;
break;
case 16384000:
sic1 = PEF2256_SIC1_SSC_16384;
break;
default:
dev_err(pef2256->dev, "Unsupported sysclk rate %lu\n", pef2256->sysclk_rate);
return -EINVAL;
}
pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_SSC_MASK, sic1);
/* data clock rate */
switch (pef2256->data_rate) {
case 2048000:
fmr1 = PEF2256_FMR1_SSD_2048;
sic1 = PEF2256_SIC1_SSD_2048;
break;
case 4096000:
fmr1 = PEF2256_FMR1_SSD_4096;
sic1 = PEF2256_SIC1_SSD_4096;
break;
case 8192000:
fmr1 = PEF2256_FMR1_SSD_8192;
sic1 = PEF2256_SIC1_SSD_8192;
break;
case 16384000:
fmr1 = PEF2256_FMR1_SSD_16384;
sic1 = PEF2256_SIC1_SSD_16384;
break;
default:
dev_err(pef2256->dev, "Unsupported data rate %u\n", pef2256->data_rate);
return -EINVAL;
}
pef2256_clrsetbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_SSD_MASK, fmr1);
pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_SSD_MASK, sic1);
/* channel phase */
pef2256_clrsetbits8(pef2256, PEF2256_SIC2, PEF2256_SIC2_SICS_MASK,
PEF2256_SIC2_SICS(pef2256->channel_phase));
return 0;
}
static void pef2256_setup_e1_signaling(struct pef2256 *pef2256)
{
/* All bits of the transmitted service word are cleared */
pef2256_write8(pef2256, PEF2256_XSW, PEF2256_XSW_XY(0x1F));
/* CAS disabled and clear spare bit values */
pef2256_write8(pef2256, PEF2256_XSP, 0x00);
if (pef2256->is_subordinate) {
/* transparent mode */
pef2256_setbits8(pef2256, PEF2256_XSW, PEF2256_XSW_XTM);
}
/* Si-Bit, Spare bit For International, FAS word */
pef2256_setbits8(pef2256, PEF2256_XSW, PEF2256_XSW_XSIS);
pef2256_setbits8(pef2256, PEF2256_XSP, PEF2256_XSP_XSIF);
/* no transparent mode active */
pef2256_write8(pef2256, PEF2256_TSWM, 0x00);
}
static void pef2256_setup_e1_errors(struct pef2256 *pef2256)
{
/* error counter latched every 1s */
pef2256_setbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_ECM);
/* error counter mode COFA */
pef2256_setbits8(pef2256, PEF2256_GCR, PEF2256_GCR_ECMC);
/* errors in service words have no influence */
pef2256_setbits8(pef2256, PEF2256_RC0, PEF2256_RC0_SWD);
/* 4 consecutive incorrect FAS causes loss of sync */
pef2256_setbits8(pef2256, PEF2256_RC0, PEF2256_RC0_ASY4);
}
static int pef2256_setup_e1(struct pef2256 *pef2256)
{
int ret;
/* Setup, Master clocking mode (GCM8..1) */
ret = pef2256_setup_gcm(pef2256);
if (ret)
return ret;
/* Select E1 mode */
pef2256_write8(pef2256, PEF2256_FMR1, 0x00);
/* internal second timer, power on */
pef2256_write8(pef2256, PEF2256_GCR, 0x00);
/* Setup line interface */
ret = pef2256_setup_e1_line(pef2256);
if (ret)
return ret;
/* Setup Loss-of-signal detection and recovery */
pef2256_setup_e1_los(pef2256);
/* Setup system interface */
ret = pef2256_setup_e1_system(pef2256);
if (ret)
return ret;
/* Setup signaling */
pef2256_setup_e1_signaling(pef2256);
/* Setup errors counters and condition */
pef2256_setup_e1_errors(pef2256);
/* status changed interrupt at both up and down */
pef2256_setbits8(pef2256, PEF2256_GCR, PEF2256_GCR_SCI);
/* Clear any ISR2 pending interrupts and unmask needed interrupts */
pef2256_read8(pef2256, PEF2256_ISR2);
pef2256_clrbits8(pef2256, PEF2256_IMR2, PEF2256_INT2_LOS | PEF2256_INT2_AIS);
/* reset lines */
pef2256_write8(pef2256, PEF2256_CMDR, PEF2256_CMDR_RRES | PEF2256_CMDR_XRES);
return 0;
}
static void pef2256_isr_default_handler(struct pef2256 *pef2256, u8 nbr, u8 isr)
{
dev_warn_ratelimited(pef2256->dev, "ISR%u: 0x%02x not handled\n", nbr, isr);
}
static bool pef2256_is_carrier_on(struct pef2256 *pef2256)
{
u8 frs0;
frs0 = pef2256_read8(pef2256, PEF2256_FRS0);
return !(frs0 & (PEF2256_FRS0_LOS | PEF2256_FRS0_AIS));
}
static void pef2256_isr2_handler(struct pef2256 *pef2256, u8 nbr, u8 isr)
{
bool carrier;
if (isr & (PEF2256_INT2_LOS | PEF2256_INT2_AIS)) {
carrier = pef2256_is_carrier_on(pef2256);
if (atomic_xchg(&pef2256->carrier, carrier) != carrier)
framer_notify_status_change(pef2256->framer);
}
}
static irqreturn_t pef2256_irq_handler(int irq, void *priv)
{
static void (*pef2256_isr_handler[])(struct pef2256 *, u8, u8) = {
[0] = pef2256_isr_default_handler,
[1] = pef2256_isr_default_handler,
[2] = pef2256_isr2_handler,
[3] = pef2256_isr_default_handler,
[4] = pef2256_isr_default_handler,
[5] = pef2256_isr_default_handler
};
struct pef2256 *pef2256 = (struct pef2256 *)priv;
u8 gis;
u8 isr;
u8 n;
gis = pef2256_read8(pef2256, PEF2256_GIS);
for (n = 0; n < ARRAY_SIZE(pef2256_isr_handler); n++) {
if (gis & PEF2256_GIS_ISR(n)) {
isr = pef2256_read8(pef2256, PEF2256_ISR(n));
pef2256_isr_handler[n](pef2256, n, isr);
}
}
return IRQ_HANDLED;
}
static int pef2256_check_rates(struct pef2256 *pef2256, unsigned long sysclk_rate,
unsigned long data_rate)
{
unsigned long rate;
switch (sysclk_rate) {
case 2048000:
case 4096000:
case 8192000:
case 16384000:
break;
default:
dev_err(pef2256->dev, "Unsupported system clock rate %lu\n", sysclk_rate);
return -EINVAL;
}
for (rate = data_rate; rate <= data_rate * 4; rate *= 2) {
if (rate == sysclk_rate)
return 0;
}
dev_err(pef2256->dev, "Unsupported data rate %lu with system clock rate %lu\n",
data_rate, sysclk_rate);
return -EINVAL;
}
static int pef2556_of_parse(struct pef2256 *pef2256, struct device_node *np)
{
int ret;
pef2256->data_rate = 2048000;
ret = of_property_read_u32(np, "lantiq,data-rate-bps", &pef2256->data_rate);
if (ret && ret != -EINVAL) {
dev_err(pef2256->dev, "%pOF: failed to read lantiq,data-rate-bps\n", np);
return ret;
}
ret = pef2256_check_rates(pef2256, pef2256->sysclk_rate, pef2256->data_rate);
if (ret)
return ret;
pef2256->is_tx_falling_edge = of_property_read_bool(np, "lantiq,clock-falling-edge");
pef2256->channel_phase = 0;
ret = of_property_read_u8(np, "lantiq,channel-phase", &pef2256->channel_phase);
if (ret && ret != -EINVAL) {
dev_err(pef2256->dev, "%pOF: failed to read lantiq,channel-phase\n",
np);
return ret;
}
if (pef2256->channel_phase >= pef2256->sysclk_rate / pef2256->data_rate) {
dev_err(pef2256->dev, "%pOF: Invalid lantiq,channel-phase %u\n",
np, pef2256->channel_phase);
return -EINVAL;
}
return 0;
}
static const struct regmap_config pef2256_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
.max_register = 0xff,
};
static const struct mfd_cell pef2256_devs[] = {
{ .name = "lantiq-pef2256-pinctrl", },
};
static int pef2256_add_audio_devices(struct pef2256 *pef2256)
{
const char *compatible = "lantiq,pef2256-codec";
struct mfd_cell *audio_devs;
struct device_node *np;
unsigned int count = 0;
unsigned int i;
int ret;
for_each_available_child_of_node(pef2256->dev->of_node, np) {
if (of_device_is_compatible(np, compatible))
count++;
}
if (!count)
return 0;
audio_devs = kcalloc(count, sizeof(*audio_devs), GFP_KERNEL);
if (!audio_devs)
return -ENOMEM;
for (i = 0; i < count; i++) {
audio_devs[i].name = "framer-codec";
audio_devs[i].of_compatible = compatible;
audio_devs[i].id = i;
}
ret = mfd_add_devices(pef2256->dev, 0, audio_devs, count, NULL, 0, NULL);
kfree(audio_devs);
return ret;
}
static int pef2256_framer_get_status(struct framer *framer, struct framer_status *status)
{
struct pef2256 *pef2256 = framer_get_drvdata(framer);
status->link_is_on = !!atomic_read(&pef2256->carrier);
return 0;
}
static int pef2256_framer_set_config(struct framer *framer, const struct framer_config *config)
{
struct pef2256 *pef2256 = framer_get_drvdata(framer);
if (config->iface != FRAMER_IFACE_E1) {
dev_err(pef2256->dev, "Only E1 line is currently supported\n");
return -EOPNOTSUPP;
}
switch (config->clock_type) {
case FRAMER_CLOCK_EXT:
pef2256->is_subordinate = true;
break;
case FRAMER_CLOCK_INT:
pef2256->is_subordinate = false;
break;
default:
return -EINVAL;
}
/* Apply the new settings */
return pef2256_setup_e1(pef2256);
}
static int pef2256_framer_get_config(struct framer *framer, struct framer_config *config)
{
struct pef2256 *pef2256 = framer_get_drvdata(framer);
config->iface = FRAMER_IFACE_E1;
config->clock_type = pef2256->is_subordinate ? FRAMER_CLOCK_EXT : FRAMER_CLOCK_INT;
config->line_clock_rate = 2048000;
return 0;
}
static const struct framer_ops pef2256_framer_ops = {
.owner = THIS_MODULE,
.get_status = pef2256_framer_get_status,
.get_config = pef2256_framer_get_config,
.set_config = pef2256_framer_set_config,
};
static int pef2256_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
unsigned long sclkr_rate, sclkx_rate;
struct framer_provider *framer_provider;
struct pef2256 *pef2256;
const char *version_txt;
void __iomem *iomem;
int ret;
int irq;
pef2256 = devm_kzalloc(&pdev->dev, sizeof(*pef2256), GFP_KERNEL);
if (!pef2256)
return -ENOMEM;
pef2256->dev = &pdev->dev;
atomic_set(&pef2256->carrier, 0);
pef2256->is_subordinate = true;
pef2256->frame_type = PEF2256_FRAME_E1_DOUBLEFRAME;
iomem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(iomem))
return PTR_ERR(iomem);
pef2256->regmap = devm_regmap_init_mmio(&pdev->dev, iomem,
&pef2256_regmap_config);
if (IS_ERR(pef2256->regmap)) {
dev_err(&pdev->dev, "Failed to initialise Regmap (%ld)\n",
PTR_ERR(pef2256->regmap));
return PTR_ERR(pef2256->regmap);
}
pef2256->mclk = devm_clk_get_enabled(&pdev->dev, "mclk");
if (IS_ERR(pef2256->mclk))
return PTR_ERR(pef2256->mclk);
pef2256->sclkr = devm_clk_get_enabled(&pdev->dev, "sclkr");
if (IS_ERR(pef2256->sclkr))
return PTR_ERR(pef2256->sclkr);
pef2256->sclkx = devm_clk_get_enabled(&pdev->dev, "sclkx");
if (IS_ERR(pef2256->sclkx))
return PTR_ERR(pef2256->sclkx);
/* Both SCLKR (receive) and SCLKX (transmit) must have the same rate,
* stored as sysclk_rate.
* The exact value will be checked at pef2256_check_rates()
*/
sclkr_rate = clk_get_rate(pef2256->sclkr);
sclkx_rate = clk_get_rate(pef2256->sclkx);
if (sclkr_rate != sclkx_rate) {
dev_err(pef2256->dev, "clk rate mismatch. sclkr %lu Hz, sclkx %lu Hz\n",
sclkr_rate, sclkx_rate);
return -EINVAL;
}
pef2256->sysclk_rate = sclkr_rate;
/* Reset the component. The MCLK clock must be active during reset */
pef2256->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(pef2256->reset_gpio))
return PTR_ERR(pef2256->reset_gpio);
if (pef2256->reset_gpio) {
gpiod_set_value_cansleep(pef2256->reset_gpio, 1);
usleep_range(10, 20);
gpiod_set_value_cansleep(pef2256->reset_gpio, 0);
usleep_range(10, 20);
}
pef2256->version = pef2256_get_version(pef2256);
switch (pef2256->version) {
case PEF2256_VERSION_1_2:
version_txt = "1.2";
break;
case PEF2256_VERSION_2_1:
version_txt = "2.1";
break;
case PEF2256_VERSION_2_2:
version_txt = "2.2";
break;
default:
return -ENODEV;
}
dev_info(pef2256->dev, "Version %s detected\n", version_txt);
ret = pef2556_of_parse(pef2256, np);
if (ret)
return ret;
/* Create the framer. It can be used on interrupts */
pef2256->framer = devm_framer_create(pef2256->dev, NULL, &pef2256_framer_ops);
if (IS_ERR(pef2256->framer))
return PTR_ERR(pef2256->framer);
framer_set_drvdata(pef2256->framer, pef2256);
/* Disable interrupts */
pef2256_write8(pef2256, PEF2256_IMR0, 0xff);
pef2256_write8(pef2256, PEF2256_IMR1, 0xff);
pef2256_write8(pef2256, PEF2256_IMR2, 0xff);
pef2256_write8(pef2256, PEF2256_IMR3, 0xff);
pef2256_write8(pef2256, PEF2256_IMR4, 0xff);
pef2256_write8(pef2256, PEF2256_IMR5, 0xff);
/* Clear any pending interrupts */
pef2256_read8(pef2256, PEF2256_ISR0);
pef2256_read8(pef2256, PEF2256_ISR1);
pef2256_read8(pef2256, PEF2256_ISR2);
pef2256_read8(pef2256, PEF2256_ISR3);
pef2256_read8(pef2256, PEF2256_ISR4);
pef2256_read8(pef2256, PEF2256_ISR5);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(pef2256->dev, irq, pef2256_irq_handler, 0, "pef2256", pef2256);
if (ret < 0)
return ret;
platform_set_drvdata(pdev, pef2256);
ret = mfd_add_devices(pef2256->dev, 0, pef2256_devs,
ARRAY_SIZE(pef2256_devs), NULL, 0, NULL);
if (ret) {
dev_err(pef2256->dev, "add devices failed (%d)\n", ret);
return ret;
}
ret = pef2256_setup_e1(pef2256);
if (ret)
return ret;
framer_provider = devm_framer_provider_of_register(pef2256->dev,
framer_provider_simple_of_xlate);
if (IS_ERR(framer_provider))
return PTR_ERR(framer_provider);
/* Add audio devices */
ret = pef2256_add_audio_devices(pef2256);
if (ret < 0) {
dev_err(pef2256->dev, "add audio devices failed (%d)\n", ret);
return ret;
}
return 0;
}
static void pef2256_remove(struct platform_device *pdev)
{
struct pef2256 *pef2256 = platform_get_drvdata(pdev);
/* Disable interrupts */
pef2256_write8(pef2256, PEF2256_IMR0, 0xff);
pef2256_write8(pef2256, PEF2256_IMR1, 0xff);
pef2256_write8(pef2256, PEF2256_IMR2, 0xff);
pef2256_write8(pef2256, PEF2256_IMR3, 0xff);
pef2256_write8(pef2256, PEF2256_IMR4, 0xff);
pef2256_write8(pef2256, PEF2256_IMR5, 0xff);
}
static const struct of_device_id pef2256_id_table[] = {
{ .compatible = "lantiq,pef2256" },
{} /* sentinel */
};
MODULE_DEVICE_TABLE(of, pef2256_id_table);
static struct platform_driver pef2256_driver = {
.driver = {
.name = "lantiq-pef2256",
.of_match_table = pef2256_id_table,
},
.probe = pef2256_probe,
.remove_new = pef2256_remove,
};
module_platform_driver(pef2256_driver);
struct regmap *pef2256_get_regmap(struct pef2256 *pef2256)
{
return pef2256->regmap;
}
EXPORT_SYMBOL_GPL(pef2256_get_regmap);
MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
MODULE_DESCRIPTION("PEF2256 driver");
MODULE_LICENSE("GPL");