// SPDX-License-Identifier: GPL-2.0-or-later /* * twl_core.c - driver for TWL4030/TWL5030/TWL60X0/TPS659x0 PM * and audio CODEC devices * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * Modifications to defer interrupt handling to a kernel thread: * Copyright (C) 2006 MontaVista Software, Inc. * * Based on tlv320aic23.c: * Copyright (c) by Kai Svahn <kai.svahn@nokia.com> * * Code cleanup and modifications to IRQ handler. * by syed khasim <x0khasim@ti.com>
*/
/* Register descriptions for audio */ #include <linux/mfd/twl4030-audio.h>
#include"twl-core.h"
/* * The TWL4030 "Triton 2" is one of a family of a multi-function "Power * Management and System Companion Device" chips originally designed for * use in OMAP2 and OMAP 3 based systems. Its control interfaces use I2C, * often at around 3 Mbit/sec, including for interrupt handling. * * This driver core provides genirq support for the interrupts emitted, * by the various modules, and exports register access primitives. * * FIXME this driver currently requires use of the first interrupt line * (and associated registers).
*/
#define DRIVER_NAME "twl"
/* Triton Core internal information (BEGIN) */
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */ #define TWL4030_BASEADD_USB 0x0000
/* Structure for each TWL4030/TWL6030 Slave */ struct twl_client { struct i2c_client *client; struct regmap *regmap;
};
/* mapping the module id to slave id and base address */ struct twl_mapping { unsignedchar sid; /* Slave ID */ unsignedchar base; /* base address */
};
struct twl_private { bool ready; /* The core driver is ready to be used */
u32 twl_idcode; /* TWL IDCODE Register value */ unsignedint twl_id;
staticstruct twl_mapping twl4030_map[] = { /* * NOTE: don't change this table without updating the * <linux/mfd/twl.h> defines for TWL4030_MODULE_* * so they continue to match the order in this table.
*/
staticbool twl4030_49_nop_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case 0x00: case 0x03: case 0x40: case 0x41: case 0x42: returnfalse; default: returntrue;
}
}
staticstruct twl_mapping twl6030_map[] = { /* * NOTE: don't change this table without updating the * <linux/mfd/twl.h> defines for TWL4030_MODULE_* * so they continue to match the order in this table.
*/
staticinlineint twl_get_num_slaves(void)
{ if (twl_class_is_4030()) return 4; /* TWL4030 class have four slave address */ else return 3; /* TWL6030 class have three slave address */
}
/** * twl_get_regmap - Get the regmap associated with the given module * @mod_no: module number * * Returns the regmap pointer or NULL in case of failure.
*/ staticstruct regmap *twl_get_regmap(u8 mod_no)
{ int sid; struct twl_client *twl;
if (unlikely(!twl_priv || !twl_priv->ready)) {
pr_err("%s: not initialized\n", DRIVER_NAME); return NULL;
} if (unlikely(mod_no >= twl_get_last_module())) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return NULL;
}
/** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number * @value: an array of num_bytes+1 containing data to write * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * Returns 0 on success or else a negative error code.
*/ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{ struct regmap *regmap = twl_get_regmap(mod_no); int ret;
if (!regmap) return -EPERM;
ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg,
value, num_bytes);
/** * twl_i2c_read - Reads a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number * @value: an array of num_bytes containing data to be read * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * Returns 0 on success or else a negative error code.
*/ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{ struct regmap *regmap = twl_get_regmap(mod_no); int ret;
if (!regmap) return -EPERM;
ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg,
value, num_bytes);
/** * twl_read_idcode_register - API to read the IDCODE register. * * Unlocks the IDCODE register and read the 32 bit value.
*/ staticint twl_read_idcode_register(void)
{ int err;
err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, TWL_EEPROM_R_UNLOCK,
REG_UNLOCK_TEST_REG); if (err) {
pr_err("TWL4030 Unable to unlock IDCODE registers -%d\n", err); goto fail;
}
err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode),
REG_IDCODE_7_0, 4); if (err) {
pr_err("TWL4030: unable to read IDCODE -%d\n", err); goto fail;
}
err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, 0x0, REG_UNLOCK_TEST_REG); if (err)
pr_err("TWL4030 Unable to relock IDCODE registers -%d\n", err);
fail: return err;
}
/** * twl_get_type - API to get TWL Si type. * * Api to get the TWL Si type from IDCODE value.
*/ int twl_get_type(void)
{ return TWL_SIL_TYPE(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_type);
/** * twl_get_version - API to get TWL Si version. * * Api to get the TWL Si version from IDCODE value.
*/ int twl_get_version(void)
{ return TWL_SIL_REV(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_version);
/** * twl_get_hfclk_rate - API to get TWL external HFCLK clock rate. * * Api to get the TWL HFCLK rate based on BOOT_CFG register.
*/ int twl_get_hfclk_rate(void)
{
u8 ctrl; int rate;
/* * These three functions initialize the on-chip clock framework, * letting it generate the right frequencies for USB, MADC, and * other purposes.
*/ staticinlineint protect_pm_master(void)
{ int e = 0;
e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
TWL4030_PM_MASTER_PROTECT_KEY); return e;
}
staticinlineint unprotect_pm_master(void)
{ int e = 0;
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
TWL4030_PM_MASTER_PROTECT_KEY);
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
staticvoid clocks_init(struct device *dev)
{ int e = 0; struct clk *osc;
u32 rate;
u8 ctrl = HFCLK_FREQ_26_MHZ;
osc = clk_get(dev, "fck"); if (IS_ERR(osc)) {
printk(KERN_WARNING "Skipping twl internal clock init and " "using bootloader value (unknown osc rate)\n"); return;
}
rate = clk_get_rate(osc);
clk_put(osc);
switch (rate) { case 19200000:
ctrl = HFCLK_FREQ_19p2_MHZ; break; case 26000000:
ctrl = HFCLK_FREQ_26_MHZ; break; case 38400000:
ctrl = HFCLK_FREQ_38p4_MHZ; break;
}
ctrl |= HIGH_PERF_SQ;
e |= unprotect_pm_master(); /* effect->MADC+USB ck en */
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= protect_pm_master();
if (e < 0)
pr_err("%s: clock init err [%d]\n", DRIVER_NAME, e);
}
/* Maybe init the T2 Interrupt subsystem */ if (client->irq) { if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
irq_base = twl4030_init_irq(&client->dev, client->irq);
} else {
irq_base = twl6030_init_irq(&client->dev, client->irq);
}
if (irq_base < 0) {
status = irq_base; goto fail;
}
}
/* * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface. * Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0, * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0. * * Also, always enable SmartReflex bit as that's needed for omaps to * do anything over I2C4 for voltage scaling even if SmartReflex * is disabled. Without the SmartReflex bit omap sys_clkreq idle * signal will never trigger for retention idle.
*/ if (twl_class_is_4030()) {
u8 temp;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.