303 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2019, Linaro Limited3 4#include <linux/clk.h>5#include <linux/gpio/consumer.h>6#include <linux/interrupt.h>7#include <linux/kernel.h>8#include <linux/mfd/core.h>9#include <linux/mfd/wcd934x/registers.h>10#include <linux/mfd/wcd934x/wcd934x.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/of_irq.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17#include <linux/slimbus.h>18 19#define WCD934X_REGMAP_IRQ_REG(_irq, _off, _mask) \20 [_irq] = { \21 .reg_offset = (_off), \22 .mask = (_mask), \23 .type = { \24 .type_reg_offset = (_off), \25 .types_supported = IRQ_TYPE_EDGE_BOTH, \26 .type_reg_mask = (_mask), \27 .type_level_low_val = (_mask), \28 .type_level_high_val = (_mask), \29 .type_falling_val = 0, \30 .type_rising_val = 0, \31 }, \32 }33 34static const struct mfd_cell wcd934x_devices[] = {35 {36 .name = "wcd934x-codec",37 }, {38 .name = "wcd934x-gpio",39 .of_compatible = "qcom,wcd9340-gpio",40 }, {41 .name = "wcd934x-soundwire",42 .of_compatible = "qcom,soundwire-v1.3.0",43 },44};45 46static const struct regmap_irq wcd934x_irqs[] = {47 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SLIMBUS, 0, BIT(0)),48 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_HPH_PA_OCPL_FAULT, 0, BIT(2)),49 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_HPH_PA_OCPR_FAULT, 0, BIT(3)),50 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_SW_DET, 1, BIT(0)),51 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, 1, BIT(1)),52 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, 1, BIT(2)),53 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, 1, BIT(3)),54 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 1, BIT(4)),55 WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SOUNDWIRE, 2, BIT(4)),56};57 58static const unsigned int wcd934x_config_regs[] = {59 WCD934X_INTR_LEVEL0,60};61 62static const struct regmap_irq_chip wcd934x_regmap_irq_chip = {63 .name = "wcd934x_irq",64 .status_base = WCD934X_INTR_PIN1_STATUS0,65 .mask_base = WCD934X_INTR_PIN1_MASK0,66 .ack_base = WCD934X_INTR_PIN1_CLEAR0,67 .num_regs = 4,68 .irqs = wcd934x_irqs,69 .num_irqs = ARRAY_SIZE(wcd934x_irqs),70 .config_base = wcd934x_config_regs,71 .num_config_bases = ARRAY_SIZE(wcd934x_config_regs),72 .num_config_regs = 4,73 .set_type_config = regmap_irq_set_type_config_simple,74};75 76static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)77{78 switch (reg) {79 case WCD934X_INTR_PIN1_STATUS0...WCD934X_INTR_PIN2_CLEAR3:80 case WCD934X_SWR_AHB_BRIDGE_RD_DATA_0:81 case WCD934X_SWR_AHB_BRIDGE_RD_DATA_1:82 case WCD934X_SWR_AHB_BRIDGE_RD_DATA_2:83 case WCD934X_SWR_AHB_BRIDGE_RD_DATA_3:84 case WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS:85 case WCD934X_ANA_MBHC_RESULT_3:86 case WCD934X_ANA_MBHC_RESULT_2:87 case WCD934X_ANA_MBHC_RESULT_1:88 case WCD934X_ANA_MBHC_MECH:89 case WCD934X_ANA_MBHC_ELECT:90 case WCD934X_ANA_MBHC_ZDET:91 case WCD934X_ANA_MICB2:92 case WCD934X_ANA_RCO:93 case WCD934X_ANA_BIAS:94 return true;95 default:96 return false;97 }98};99 100static const struct regmap_range_cfg wcd934x_ranges[] = {101 { .name = "WCD934X",102 .range_min = 0x0,103 .range_max = WCD934X_MAX_REGISTER,104 .selector_reg = WCD934X_SEL_REGISTER,105 .selector_mask = WCD934X_SEL_MASK,106 .selector_shift = WCD934X_SEL_SHIFT,107 .window_start = WCD934X_WINDOW_START,108 .window_len = WCD934X_WINDOW_LENGTH,109 },110};111 112static const struct regmap_config wcd934x_regmap_config = {113 .reg_bits = 16,114 .val_bits = 8,115 .cache_type = REGCACHE_MAPLE,116 .max_register = 0xffff,117 .can_multi_write = true,118 .ranges = wcd934x_ranges,119 .num_ranges = ARRAY_SIZE(wcd934x_ranges),120 .volatile_reg = wcd934x_is_volatile_register,121};122 123static int wcd934x_bring_up(struct wcd934x_ddata *ddata)124{125 struct regmap *regmap = ddata->regmap;126 u16 id_minor, id_major;127 int ret;128 129 ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,130 (u8 *)&id_minor, sizeof(u16));131 if (ret)132 return ret;133 134 ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,135 (u8 *)&id_major, sizeof(u16));136 if (ret)137 return ret;138 139 dev_info(ddata->dev, "WCD934x chip id major 0x%x, minor 0x%x\n",140 id_major, id_minor);141 142 regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);143 regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);144 regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);145 /* Add 1msec delay for VOUT to settle */146 usleep_range(1000, 1100);147 regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);148 regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);149 regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);150 regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);151 regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);152 153 return 0;154}155 156static int wcd934x_slim_status_up(struct slim_device *sdev)157{158 struct device *dev = &sdev->dev;159 struct wcd934x_ddata *ddata;160 int ret;161 162 ddata = dev_get_drvdata(dev);163 164 ddata->regmap = regmap_init_slimbus(sdev, &wcd934x_regmap_config);165 if (IS_ERR(ddata->regmap)) {166 dev_err(dev, "Error allocating slim regmap\n");167 return PTR_ERR(ddata->regmap);168 }169 170 ret = wcd934x_bring_up(ddata);171 if (ret) {172 dev_err(dev, "Failed to bring up WCD934X: err = %d\n", ret);173 return ret;174 }175 176 ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq,177 IRQF_TRIGGER_HIGH, 0,178 &wcd934x_regmap_irq_chip,179 &ddata->irq_data);180 if (ret) {181 dev_err(dev, "Failed to add IRQ chip: err = %d\n", ret);182 return ret;183 }184 185 ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, wcd934x_devices,186 ARRAY_SIZE(wcd934x_devices), NULL, 0, NULL);187 if (ret) {188 dev_err(dev, "Failed to add child devices: err = %d\n",189 ret);190 return ret;191 }192 193 return ret;194}195 196static int wcd934x_slim_status(struct slim_device *sdev,197 enum slim_device_status status)198{199 switch (status) {200 case SLIM_DEVICE_STATUS_UP:201 return wcd934x_slim_status_up(sdev);202 case SLIM_DEVICE_STATUS_DOWN:203 mfd_remove_devices(&sdev->dev);204 break;205 default:206 return -EINVAL;207 }208 209 return 0;210}211 212static int wcd934x_slim_probe(struct slim_device *sdev)213{214 struct device *dev = &sdev->dev;215 struct device_node *np = dev->of_node;216 struct wcd934x_ddata *ddata;217 struct gpio_desc *reset_gpio;218 int ret;219 220 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);221 if (!ddata)222 return -ENOMEM;223 224 ddata->irq = of_irq_get(np, 0);225 if (ddata->irq < 0)226 return dev_err_probe(ddata->dev, ddata->irq,227 "Failed to get IRQ\n");228 229 ddata->extclk = devm_clk_get(dev, "extclk");230 if (IS_ERR(ddata->extclk))231 return dev_err_probe(dev, PTR_ERR(ddata->extclk),232 "Failed to get extclk");233 234 ddata->supplies[0].supply = "vdd-buck";235 ddata->supplies[1].supply = "vdd-buck-sido";236 ddata->supplies[2].supply = "vdd-tx";237 ddata->supplies[3].supply = "vdd-rx";238 ddata->supplies[4].supply = "vdd-io";239 240 ret = regulator_bulk_get(dev, WCD934X_MAX_SUPPLY, ddata->supplies);241 if (ret)242 return dev_err_probe(dev, ret, "Failed to get supplies\n");243 244 ret = regulator_bulk_enable(WCD934X_MAX_SUPPLY, ddata->supplies);245 if (ret)246 return dev_err_probe(dev, ret, "Failed to enable supplies\n");247 248 /*249 * For WCD934X, it takes about 600us for the Vout_A and250 * Vout_D to be ready after BUCK_SIDO is powered up.251 * SYS_RST_N shouldn't be pulled high during this time252 */253 usleep_range(600, 650);254 reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);255 if (IS_ERR(reset_gpio)) {256 ret = dev_err_probe(dev, PTR_ERR(reset_gpio),257 "Failed to get reset gpio\n");258 goto err_disable_regulators;259 }260 msleep(20);261 gpiod_set_value(reset_gpio, 1);262 msleep(20);263 264 ddata->dev = dev;265 dev_set_drvdata(dev, ddata);266 267 return 0;268 269err_disable_regulators:270 regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies);271 return ret;272}273 274static void wcd934x_slim_remove(struct slim_device *sdev)275{276 struct wcd934x_ddata *ddata = dev_get_drvdata(&sdev->dev);277 278 regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies);279 mfd_remove_devices(&sdev->dev);280}281 282static const struct slim_device_id wcd934x_slim_id[] = {283 { SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9340,284 SLIM_DEV_IDX_WCD9340, SLIM_DEV_INSTANCE_ID_WCD9340 },285 {}286};287 288static struct slim_driver wcd934x_slim_driver = {289 .driver = {290 .name = "wcd934x-slim",291 },292 .probe = wcd934x_slim_probe,293 .remove = wcd934x_slim_remove,294 .device_status = wcd934x_slim_status,295 .id_table = wcd934x_slim_id,296};297 298module_slim_driver(wcd934x_slim_driver);299MODULE_DESCRIPTION("WCD934X slim driver");300MODULE_LICENSE("GPL v2");301MODULE_ALIAS("slim:217:250:*");302MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");303