6db4831e98
Android 14
513 lines
13 KiB
C
513 lines
13 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
#include <mtk_spi.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/interrupt.h>
|
|
#ifdef CONFIG_RT_REGMAP
|
|
#include <mt-plat/rt-regmap.h>
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
#include "rt5734-spi.h"
|
|
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
#include <mach/mtk_pmic_ipi.h>
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
|
|
#define RT5734_DRV_VERSION "1.0.0_MTK"
|
|
#define RT5734_IRQ_ENABLE 0
|
|
|
|
static int rt5734_read_device(void *client, u32 addr, int len, void *dst)
|
|
{
|
|
int ret;
|
|
#ifdef CONIFG_MTK_TINYSYS_SSPM_SUPPORT
|
|
pr_notice("%s not support for sspm\n", __func__);
|
|
return 0;
|
|
#else
|
|
struct spi_device *spi = (struct spi_device *)client;
|
|
struct spi_transfer xfer = {0,}; /* must init spi_transfer here */
|
|
struct spi_message msg;
|
|
u32 tx_buf;
|
|
u32 rx_buf;
|
|
|
|
if (len != 1) {
|
|
pr_notice("%s not support multi read now\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* LSM first, TX: buf, size, reg, 89 */
|
|
tx_buf = 0x00010080;
|
|
rx_buf = 0xffffffff;
|
|
|
|
tx_buf |= addr << 8;
|
|
|
|
xfer.tx_buf = &tx_buf;
|
|
xfer.rx_buf = &rx_buf;
|
|
xfer.len = 4;
|
|
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&xfer, &msg);
|
|
ret = spi_sync(spi, &msg);
|
|
if (ret < 0 || rx_buf == 0xffffffff)
|
|
return ret;
|
|
|
|
*(unsigned char *)dst = (rx_buf & 0xff000000) >> 24;
|
|
#if 0
|
|
pr_info("%s addr 0x%02x = 0x%02x\n", __func__, addr, *val);
|
|
pr_info("%s tx_buf = 0x%08x\n", __func__, tx_buf);
|
|
pr_info("%s rx_buf = 0x%08x\n", __func__, rx_buf);
|
|
#endif
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
int rt5734_write_device(void *client,
|
|
uint32_t addr, int len, const void *src)
|
|
{
|
|
int ret;
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
pr_notice("%s not support sspm\n", __func__);
|
|
return 0;
|
|
#else
|
|
struct spi_device *spi = (struct spi_device *)client;
|
|
struct spi_transfer xfer = {0,}; /* must init spi_transfer here */
|
|
struct spi_message msg;
|
|
unsigned char regval;
|
|
u32 tx_buf;
|
|
u32 rx_buf;
|
|
|
|
if (len != 1) {
|
|
pr_notice("%s not support multi write now\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
tx_buf = 0x00010000;
|
|
rx_buf = 0xffffffff;
|
|
|
|
regval = *(unsigned char *)src;
|
|
tx_buf |= ((regval << 24) | (addr << 8));
|
|
|
|
xfer.tx_buf = &tx_buf;
|
|
xfer.rx_buf = &rx_buf;
|
|
xfer.len = 4;
|
|
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&xfer, &msg);
|
|
ret = spi_sync(spi, &msg);
|
|
if (ret < 0 || rx_buf == 0xffffffff)
|
|
return ret;
|
|
|
|
#if 0
|
|
pr_info("%s addr 0x%02x = 0x%02x\n", __func__, addr, value);
|
|
pr_info("%s tx_buf = 0x%08x\n", __func__, tx_buf);
|
|
pr_info("%s rx_buf = 0x%08x\n", __func__, rx_buf);
|
|
#endif
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
RT_REG_DECL(RT5734_CHIPNAME_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_FLT_RECORDTEMP_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_IRQ_MASK_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK1_DCM_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK1_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK1_MODE_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK1_RSPCFG1_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK2_DCM_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK2_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK2_MODE_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK2_RSPCFG1_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK3_DCM_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK3_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK3_MODE_R, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT5734_BUCK3_RSPCFG1_R, 1, RT_VOLATILE, {});
|
|
|
|
static const rt_register_map_t rt5734_regmap[] = {
|
|
RT_REG(RT5734_CHIPNAME_R),
|
|
RT_REG(RT5734_FLT_RECORDTEMP_R),
|
|
RT_REG(RT5734_IRQ_MASK_R),
|
|
RT_REG(RT5734_BUCK1_DCM_R),
|
|
RT_REG(RT5734_BUCK1_R),
|
|
RT_REG(RT5734_BUCK1_MODE_R),
|
|
RT_REG(RT5734_BUCK1_RSPCFG1_R),
|
|
RT_REG(RT5734_BUCK2_DCM_R),
|
|
RT_REG(RT5734_BUCK2_R),
|
|
RT_REG(RT5734_BUCK2_MODE_R),
|
|
RT_REG(RT5734_BUCK2_RSPCFG1_R),
|
|
RT_REG(RT5734_BUCK3_DCM_R),
|
|
RT_REG(RT5734_BUCK3_R),
|
|
RT_REG(RT5734_BUCK3_MODE_R),
|
|
RT_REG(RT5734_BUCK3_RSPCFG1_R),
|
|
};
|
|
|
|
static struct rt_regmap_properties rt5734_regmap_props = {
|
|
.name = "rt5734",
|
|
.aliases = "rt5734",
|
|
.register_num = ARRAY_SIZE(rt5734_regmap),
|
|
.rm = rt5734_regmap,
|
|
.rt_regmap_mode = RT_CACHE_DISABLE,
|
|
};
|
|
|
|
static struct rt_regmap_fops rt5734_regmap_fops = {
|
|
.read_device = rt5734_read_device,
|
|
.write_device = rt5734_write_device,
|
|
};
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
|
|
int rt5734_read_byte(void *client, uint32_t addr, uint32_t *value)
|
|
{
|
|
int ret;
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
pr_notice("%s not support for sspm\n", __func__);
|
|
ret = 0;
|
|
#else
|
|
struct spi_device *spi = (struct spi_device *)client;
|
|
struct rt5734_chip *chip = spi_get_drvdata(spi);
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
ret = rt_regmap_block_read(chip->regmap_dev, addr, 1, value);
|
|
#else
|
|
ret = rt5734_read_device(chip->spi, addr, 1, value);
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
if (ret < 0)
|
|
pr_notice("%s read addr0x%02x fail\n", __func__, addr);
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(rt5734_read_byte);
|
|
|
|
int rt5734_write_byte(void *client, uint32_t addr, uint32_t data)
|
|
{
|
|
int ret = 0;
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
pr_notice("%s not support for sspm\n", __func__);
|
|
ret = 0;
|
|
#else
|
|
struct spi_device *spi = (struct spi_device *)client;
|
|
struct rt5734_chip *chip = spi_get_drvdata(spi);
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
ret = rt_regmap_block_write(chip->regmap_dev, addr, 1, &data);
|
|
#else
|
|
ret = rt5734_write_device(chip->spi, addr, 1, &data);
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
if (ret < 0)
|
|
pr_notice("%s write addr0x%02x fail\n", __func__, addr);
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
int rt5734_assign_bit(void *client,
|
|
uint32_t reg, uint32_t mask, uint32_t data)
|
|
{
|
|
int ret = 0;
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
pr_notice("%s not support for sspm\n", __func__);
|
|
return 0;
|
|
#else
|
|
struct spi_device *spi = (struct spi_device *)client;
|
|
struct rt5734_chip *ri = spi_get_drvdata(spi);
|
|
unsigned char tmp = 0;
|
|
uint32_t regval = 0;
|
|
|
|
mutex_lock(&ri->io_lock);
|
|
ret = rt5734_read_byte(spi, reg, ®val);
|
|
if (ret < 0) {
|
|
pr_notice("%s fail reg0x%02x data0x%02x\n",
|
|
__func__, reg, data);
|
|
goto OUT_ASSIGN;
|
|
}
|
|
tmp = ((regval & 0xff) & ~mask);
|
|
tmp |= (data & mask);
|
|
ret = rt5734_write_byte(spi, reg, tmp);
|
|
if (ret < 0)
|
|
pr_notice("%s fail reg0x%02x data0x%02x\n",
|
|
__func__, reg, tmp);
|
|
OUT_ASSIGN:
|
|
mutex_unlock(&ri->io_lock);
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(rt5734_assign_bit);
|
|
|
|
static struct mt_chip_conf rt5734_spi_config = {
|
|
/* setuptime, holdtime --> timing for waveform of SPI */
|
|
.setuptime = 3,
|
|
.holdtime = 3,
|
|
/* high_time, low_time --> set SCK */
|
|
.high_time = 10,
|
|
.low_time = 10,
|
|
/* CS pin idle time */
|
|
.cs_idletime = 2,
|
|
/* cpol, cpha -->set type */
|
|
.cpol = 0,
|
|
.cpha = 0,
|
|
/* rx_mlsb, tx_mlsb --> MSB first or not */
|
|
.rx_mlsb = 1,
|
|
.tx_mlsb = 1,
|
|
/* tx_endian, rx_endian -->
|
|
* Defines whether to reverse the endian order
|
|
*/
|
|
.tx_endian = 0,
|
|
.rx_endian = 0,
|
|
/* com_mod --> FIFO/DMA mode */
|
|
.com_mod = FIFO_TRANSFER,
|
|
/* pause --> if want to always let CS active, set this flag to 1*/
|
|
.pause = 1,
|
|
/* tckdly --> tune timing */
|
|
.tckdly = 0,
|
|
|
|
/* ?? */
|
|
.ulthgh_thrsh = 0,
|
|
.cs_pol = 0,
|
|
.sample_sel = 0,
|
|
.finish_intr = 1,
|
|
.deassert = 0,
|
|
.ulthigh = 0,
|
|
};
|
|
|
|
static void rt5734_spi_init(struct spi_device *spi)
|
|
{
|
|
pr_info("%s inited\n", __func__);
|
|
spi->bits_per_word = 32;
|
|
spi->controller_data = &rt5734_spi_config;
|
|
mdelay(100);
|
|
}
|
|
|
|
#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
static void rt5734_reg_init(struct spi_device *spi)
|
|
{
|
|
}
|
|
|
|
static int rt5734_check_id(struct spi_device *spi)
|
|
{
|
|
int ret;
|
|
unsigned char data;
|
|
|
|
ret = rt5734_read_device(spi, RT5734_CHIPNAME_R, 1, &data);
|
|
if (ret < 0) {
|
|
pr_notice("%s IO fail\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (data != RT5734_CHIPNAME) {
|
|
pr_notice("%s ID(0x%02x) not match\n", __func__, data);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
|
|
#if RT5734_IRQ_ENABLE
|
|
enum {
|
|
RT5734_EVT_OT_SHUTDOWN_FALLING = 1,
|
|
RT5734_EVT_OT_SHUTDOWN_RISING,
|
|
RT5734_EVT_MAX,
|
|
};
|
|
|
|
static void rt5734_irq_evt_handler(void *info, int eventno)
|
|
{
|
|
struct rt5734_chip *chip = info;
|
|
|
|
dev_info(chip->dev, "%s eventno = %d\n", __func__, eventno);
|
|
switch (eventno) {
|
|
case RT5734_EVT_OT_SHUTDOWN_RISING:
|
|
pr_notice("%s Enter OT Shutdown\n", __func__);
|
|
break;
|
|
case RT5734_EVT_OT_SHUTDOWN_FALLING:
|
|
pr_notice("%s Exit OT Shutdown\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
typedef void (*rt_irq_handler)(void *info, int eventno);
|
|
|
|
static rt_irq_handler rt5734_handler[RT5734_EVT_MAX] = {
|
|
[RT5734_EVT_OT_SHUTDOWN_RISING] = rt5734_irq_evt_handler,
|
|
[RT5734_EVT_OT_SHUTDOWN_FALLING] = rt5734_irq_evt_handler,
|
|
};
|
|
|
|
static irqreturn_t rt5734_irq_handler(int irqno, void *param)
|
|
{
|
|
struct rt5734_chip *chip = param;
|
|
uint32_t regval = 0;
|
|
int ret, i;
|
|
|
|
ret = rt5734_read_byte(chip->spi,
|
|
RT5734_FLT_RECORDTEMP_R, ®val);
|
|
if (ret < 0) {
|
|
pr_notice("%s get irq regval fail\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
if (regval) {
|
|
pr_info("%s thermal event 0x%02x\n", __func__, regval);
|
|
for (i = 0; i < RT5734_EVT_MAX; i++) {
|
|
if ((regval & (1 << i)) && rt5734_handler[i])
|
|
rt5734_handler[i](chip, i);
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int rt5734_init_irq(struct rt5734_chip *chip, struct device *dev)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
int ret;
|
|
uint32_t regval = 0;
|
|
|
|
if (np)
|
|
chip->irq = irq_of_parse_and_map(np, 0);
|
|
else {
|
|
pr_notice("%s no dts node\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* mask IRQ first */
|
|
ret = rt5734_write_byte(chip->spi, RT5734_IRQ_MASK_R, 0x83);
|
|
if (ret < 0) {
|
|
pr_notice("%s mask IRQ fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
/* clear IRQ */
|
|
rt5734_read_byte(chip->spi, RT5734_FLT_RECORDTEMP_R, ®val);
|
|
|
|
ret = devm_request_threaded_irq(chip->dev, chip->irq,
|
|
rt5734_irq_handler, NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
|
|
"rt5734-irq", chip);
|
|
if (ret < 0) {
|
|
pr_notice("%s request irq fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
/* unmask IRQ */
|
|
ret = rt5734_write_byte(chip->spi, RT5734_IRQ_MASK_R, 0x00);
|
|
if (ret < 0) {
|
|
pr_notice("%s unmask IRQ fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
enable_irq_wake(chip->irq);
|
|
return 0;
|
|
}
|
|
#endif /* if RT5734_IRQ_ENABLE */
|
|
|
|
static int rt5734_spi_probe(struct spi_device *spi)
|
|
{
|
|
struct rt5734_chip *chip;
|
|
int ret;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
chip = devm_kzalloc(&spi->dev,
|
|
sizeof(struct rt5734_chip), GFP_KERNEL);
|
|
|
|
chip->spi = spi;
|
|
chip->dev = &spi->dev;
|
|
mutex_init(&chip->io_lock);
|
|
#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
spi_set_drvdata(spi, chip);
|
|
#else
|
|
pr_info("%s SSPM not need spi_set_drvdata\n", __func__);
|
|
#endif /* if not CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
|
|
|
|
rt5734_spi_init(spi);
|
|
|
|
#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
ret = rt5734_check_id(spi);
|
|
if (ret < 0)
|
|
return ret;
|
|
#else
|
|
pr_info("%s SSPM not need check_id\n", __func__);
|
|
#endif /*-- !CONFIG_MTK_TINYSYS_SSPM_SUPPORT--*/
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
chip->regmap_dev = rt_regmap_device_register_ex(&rt5734_regmap_props,
|
|
&rt5734_regmap_fops, &spi->dev, spi, -1, chip);
|
|
if (!chip->regmap_dev) {
|
|
pr_notice("%s register regmap fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
#endif /* #ifdef CONFIG_RT_REGMAP */
|
|
|
|
#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
rt5734_reg_init(spi);
|
|
#else
|
|
pr_info("%s SSPM not need reg_init\n", __func__);
|
|
#endif /*-- !CONFIG_MTK_TINYSYS_SSPM_SUPPORT--*/
|
|
|
|
ret = rt5734_regulator_init(chip);
|
|
if (ret < 0) {
|
|
RT5734_pr_notice("%s regulator init fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_info("%s --OK!!--\n", __func__);
|
|
return 0;
|
|
|
|
#if RT5734_IRQ_ENABLE
|
|
fail_irq:
|
|
#ifdef CONFIG_RT_REGMAP
|
|
rt_regmap_device_unregister(chip->regmap_dev);
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
#endif /* CONFIG_IRQ_ENABLE */
|
|
return ret;
|
|
}
|
|
|
|
static int rt5734_spi_remove(struct spi_device *spi)
|
|
{
|
|
struct rt5734_chip *chip = spi_get_drvdata(spi);
|
|
|
|
if (chip) {
|
|
rt5734_regulator_deinit(chip);
|
|
mutex_destroy(&chip->io_lock);
|
|
#ifdef CONFIG_RT_REGMAP
|
|
rt_regmap_device_unregister(chip->regmap_dev);
|
|
#endif /* #ifdef CONFIG_RT_REGMAP */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id rt5734_id_table[] = {
|
|
{.compatible = "mediatek,rt5734",},
|
|
};
|
|
|
|
static struct spi_driver rt5734_spi_driver = {
|
|
.driver = {
|
|
.name = "rt5734",
|
|
.bus = &spi_bus_type,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = rt5734_id_table,
|
|
},
|
|
.probe = rt5734_spi_probe,
|
|
.remove = rt5734_spi_remove,
|
|
};
|
|
|
|
static int __init rt5734_init(void)
|
|
{
|
|
pr_notice("%s ver(%s)\n", __func__, RT5734_DRV_VERSION);
|
|
return spi_register_driver(&rt5734_spi_driver);
|
|
}
|
|
|
|
static void __exit rt5734_exit(void)
|
|
{
|
|
spi_unregister_driver(&rt5734_spi_driver);
|
|
}
|
|
subsys_initcall(rt5734_init);
|
|
module_exit(rt5734_exit);
|
|
|
|
MODULE_DESCRIPTION("RT5734 Regulator Driver");
|
|
MODULE_VERSION(RT5734_DRV_VERSION);
|
|
MODULE_AUTHOR("Sakya <jeff_chang@richtek.com>");
|
|
MODULE_LICENSE("GPL");
|