silicom-platform.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. // SPDX-License-Identifier: GPL-2.0+
  2. //
  3. // silicom-platform.c - Silicom MEC170x platform driver
  4. //
  5. // Copyright (C) 2023 Henry Shi <henrys@silicom-usa.com>
  6. #include <linux/bitfield.h>
  7. #include <linux/bits.h>
  8. #include <linux/dmi.h>
  9. #include <linux/hwmon.h>
  10. #include <linux/init.h>
  11. #include <linux/ioport.h>
  12. #include <linux/io.h>
  13. #include <linux/kernel.h>
  14. #include <linux/kobject.h>
  15. #include <linux/led-class-multicolor.h>
  16. #include <linux/module.h>
  17. #include <linux/mutex.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/string.h>
  20. #include <linux/sysfs.h>
  21. #include <linux/units.h>
  22. #include <linux/gpio/driver.h>
  23. #define MEC_POWER_CYCLE_ADDR 0x24
  24. #define MEC_EFUSE_LSB_ADDR 0x28
  25. #define MEC_GPIO_IN_POS 0x08
  26. #define MEC_IO_BASE 0x0800
  27. #define MEC_IO_LEN 0x8
  28. #define IO_REG_BANK 0x0
  29. #define DEFAULT_CHAN_LO 0
  30. #define DEFAULT_CHAN_HI 0
  31. #define DEFAULT_CHAN_LO_T 0xc
  32. #define MEC_ADDR (MEC_IO_BASE + 0x02)
  33. #define EC_ADDR_LSB MEC_ADDR
  34. #define SILICOM_MEC_MAGIC 0x5a
  35. #define MEC_PORT_CHANNEL_MASK GENMASK(2, 0)
  36. #define MEC_PORT_DWORD_OFFSET GENMASK(31, 3)
  37. #define MEC_DATA_OFFSET_MASK GENMASK(1, 0)
  38. #define MEC_PORT_OFFSET_MASK GENMASK(7, 2)
  39. #define MEC_TEMP_LOC GENMASK(31, 16)
  40. #define MEC_VERSION_LOC GENMASK(15, 8)
  41. #define MEC_VERSION_MAJOR GENMASK(15, 14)
  42. #define MEC_VERSION_MINOR GENMASK(13, 8)
  43. #define EC_ADDR_MSB (MEC_IO_BASE + 0x3)
  44. #define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset))
  45. #define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
  46. #define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
  47. static DEFINE_MUTEX(mec_io_mutex);
  48. static unsigned int efuse_status;
  49. static unsigned int mec_uc_version;
  50. static unsigned int power_cycle;
  51. static const struct hwmon_channel_info *silicom_fan_control_info[] = {
  52. HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
  53. HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
  54. NULL
  55. };
  56. struct silicom_platform_info {
  57. int io_base;
  58. int io_len;
  59. struct led_classdev_mc *led_info;
  60. struct gpio_chip *gpiochip;
  61. u8 *gpio_channels;
  62. u16 ngpio;
  63. };
  64. static const char * const plat_0222_gpio_names[] = {
  65. "AUTOM0_SFP_TX_FAULT",
  66. "SLOT2_LED_OUT",
  67. "SIM_M2_SLOT2_B_DET",
  68. "SIM_M2_SLOT2_A_DET",
  69. "SLOT1_LED_OUT",
  70. "SIM_M2_SLOT1_B_DET",
  71. "SIM_M2_SLOT1_A_DET",
  72. "SLOT0_LED_OUT",
  73. "WAN_SFP0_RX_LOS",
  74. "WAN_SFP0_PRSNT_N",
  75. "WAN_SFP0_TX_FAULT",
  76. "AUTOM1_SFP_RX_LOS",
  77. "AUTOM1_SFP_PRSNT_N",
  78. "AUTOM1_SFP_TX_FAULT",
  79. "AUTOM0_SFP_RX_LOS",
  80. "AUTOM0_SFP_PRSNT_N",
  81. "WAN_SFP1_RX_LOS",
  82. "WAN_SFP1_PRSNT_N",
  83. "WAN_SFP1_TX_FAULT",
  84. "SIM_M2_SLOT1_MUX_SEL",
  85. "W_DISABLE_M2_SLOT1_N",
  86. "W_DISABLE_MPCIE_SLOT0_N",
  87. "W_DISABLE_M2_SLOT0_N",
  88. "BT_COMMAND_MODE",
  89. "WAN_SFP1_TX_DISABLE",
  90. "WAN_SFP0_TX_DISABLE",
  91. "AUTOM1_SFP_TX_DISABLE",
  92. "AUTOM0_SFP_TX_DISABLE",
  93. "SIM_M2_SLOT2_MUX_SEL",
  94. "W_DISABLE_M2_SLOT2_N",
  95. "RST_CTL_M2_SLOT_1_N",
  96. "RST_CTL_M2_SLOT_2_N",
  97. "PM_USB_PWR_EN_BOT",
  98. "PM_USB_PWR_EN_TOP",
  99. };
  100. static u8 plat_0222_gpio_channels[] = {
  101. OFFSET_BIT_TO_CHANNEL(0x00, 0),
  102. OFFSET_BIT_TO_CHANNEL(0x00, 1),
  103. OFFSET_BIT_TO_CHANNEL(0x00, 2),
  104. OFFSET_BIT_TO_CHANNEL(0x00, 3),
  105. OFFSET_BIT_TO_CHANNEL(0x00, 4),
  106. OFFSET_BIT_TO_CHANNEL(0x00, 5),
  107. OFFSET_BIT_TO_CHANNEL(0x00, 6),
  108. OFFSET_BIT_TO_CHANNEL(0x00, 7),
  109. OFFSET_BIT_TO_CHANNEL(0x01, 0),
  110. OFFSET_BIT_TO_CHANNEL(0x01, 1),
  111. OFFSET_BIT_TO_CHANNEL(0x01, 2),
  112. OFFSET_BIT_TO_CHANNEL(0x01, 3),
  113. OFFSET_BIT_TO_CHANNEL(0x01, 4),
  114. OFFSET_BIT_TO_CHANNEL(0x01, 5),
  115. OFFSET_BIT_TO_CHANNEL(0x01, 6),
  116. OFFSET_BIT_TO_CHANNEL(0x01, 7),
  117. OFFSET_BIT_TO_CHANNEL(0x02, 0),
  118. OFFSET_BIT_TO_CHANNEL(0x02, 1),
  119. OFFSET_BIT_TO_CHANNEL(0x02, 2),
  120. OFFSET_BIT_TO_CHANNEL(0x09, 0),
  121. OFFSET_BIT_TO_CHANNEL(0x09, 1),
  122. OFFSET_BIT_TO_CHANNEL(0x09, 2),
  123. OFFSET_BIT_TO_CHANNEL(0x09, 3),
  124. OFFSET_BIT_TO_CHANNEL(0x0a, 0),
  125. OFFSET_BIT_TO_CHANNEL(0x0a, 1),
  126. OFFSET_BIT_TO_CHANNEL(0x0a, 2),
  127. OFFSET_BIT_TO_CHANNEL(0x0a, 3),
  128. OFFSET_BIT_TO_CHANNEL(0x0a, 4),
  129. OFFSET_BIT_TO_CHANNEL(0x0a, 5),
  130. OFFSET_BIT_TO_CHANNEL(0x0a, 6),
  131. OFFSET_BIT_TO_CHANNEL(0x0b, 0),
  132. OFFSET_BIT_TO_CHANNEL(0x0b, 1),
  133. OFFSET_BIT_TO_CHANNEL(0x0b, 2),
  134. OFFSET_BIT_TO_CHANNEL(0x0b, 3),
  135. };
  136. static struct platform_device *silicom_platform_dev;
  137. static struct led_classdev_mc *silicom_led_info __initdata;
  138. static struct gpio_chip *silicom_gpiochip __initdata;
  139. static u8 *silicom_gpio_channels __initdata;
  140. static int silicom_mec_port_get(unsigned int offset)
  141. {
  142. unsigned short mec_data_addr;
  143. unsigned short mec_port_addr;
  144. u8 reg;
  145. mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK;
  146. mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK;
  147. mutex_lock(&mec_io_mutex);
  148. outb(mec_port_addr, MEC_ADDR);
  149. reg = inb(MEC_DATA_OFFSET(mec_data_addr));
  150. mutex_unlock(&mec_io_mutex);
  151. return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01;
  152. }
  153. static enum led_brightness silicom_mec_led_get(int channel)
  154. {
  155. /* Outputs are active low */
  156. return silicom_mec_port_get(channel) ? LED_OFF : LED_ON;
  157. }
  158. static void silicom_mec_port_set(int channel, int on)
  159. {
  160. unsigned short mec_data_addr;
  161. unsigned short mec_port_addr;
  162. u8 reg;
  163. mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK;
  164. mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK;
  165. mutex_lock(&mec_io_mutex);
  166. outb(mec_port_addr, MEC_ADDR);
  167. reg = inb(MEC_DATA_OFFSET(mec_data_addr));
  168. /* Outputs are active low, so clear the bit for on, or set it for off */
  169. if (on)
  170. reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK));
  171. else
  172. reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK);
  173. outb(reg, MEC_DATA_OFFSET(mec_data_addr));
  174. mutex_unlock(&mec_io_mutex);
  175. }
  176. static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev)
  177. {
  178. struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
  179. enum led_brightness brightness = LED_OFF;
  180. int i;
  181. for (i = 0; i < mc_cdev->num_colors; i++) {
  182. mc_cdev->subled_info[i].brightness =
  183. silicom_mec_led_get(mc_cdev->subled_info[i].channel);
  184. /* Mark the overall brightness as LED_ON if any of the subleds are on */
  185. if (mc_cdev->subled_info[i].brightness != LED_OFF)
  186. brightness = LED_ON;
  187. }
  188. return brightness;
  189. }
  190. static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
  191. enum led_brightness brightness)
  192. {
  193. struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
  194. int i;
  195. led_mc_calc_color_components(mc_cdev, brightness);
  196. for (i = 0; i < mc_cdev->num_colors; i++) {
  197. silicom_mec_port_set(mc_cdev->subled_info[i].channel,
  198. mc_cdev->subled_info[i].brightness);
  199. }
  200. }
  201. static int silicom_gpio_get_direction(struct gpio_chip *gc,
  202. unsigned int offset)
  203. {
  204. u8 *channels = gpiochip_get_data(gc);
  205. /* Input registers have offsets between [0x00, 0x07] */
  206. if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS)
  207. return GPIO_LINE_DIRECTION_IN;
  208. return GPIO_LINE_DIRECTION_OUT;
  209. }
  210. static int silicom_gpio_direction_input(struct gpio_chip *gc,
  211. unsigned int offset)
  212. {
  213. int direction = silicom_gpio_get_direction(gc, offset);
  214. return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL;
  215. }
  216. static int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset,
  217. int value)
  218. {
  219. u8 *channels = gpiochip_get_data(gc);
  220. int channel = channels[offset];
  221. silicom_mec_port_set(channel, !value);
  222. return 0;
  223. }
  224. static int silicom_gpio_direction_output(struct gpio_chip *gc,
  225. unsigned int offset,
  226. int value)
  227. {
  228. int direction = silicom_gpio_get_direction(gc, offset);
  229. if (direction == GPIO_LINE_DIRECTION_IN)
  230. return -EINVAL;
  231. silicom_gpio_set(gc, offset, value);
  232. return 0;
  233. }
  234. static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset)
  235. {
  236. u8 *channels = gpiochip_get_data(gc);
  237. int channel = channels[offset];
  238. return silicom_mec_port_get(channel);
  239. }
  240. static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = {
  241. {
  242. .color_index = LED_COLOR_ID_WHITE,
  243. .brightness = 1,
  244. .intensity = 0,
  245. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7),
  246. },
  247. {
  248. .color_index = LED_COLOR_ID_YELLOW,
  249. .brightness = 1,
  250. .intensity = 0,
  251. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6),
  252. },
  253. {
  254. .color_index = LED_COLOR_ID_RED,
  255. .brightness = 1,
  256. .intensity = 0,
  257. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5),
  258. },
  259. };
  260. static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = {
  261. {
  262. .color_index = LED_COLOR_ID_WHITE,
  263. .brightness = 1,
  264. .intensity = 0,
  265. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4),
  266. },
  267. {
  268. .color_index = LED_COLOR_ID_AMBER,
  269. .brightness = 1,
  270. .intensity = 0,
  271. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3),
  272. },
  273. {
  274. .color_index = LED_COLOR_ID_RED,
  275. .brightness = 1,
  276. .intensity = 0,
  277. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2),
  278. },
  279. };
  280. static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = {
  281. {
  282. .color_index = LED_COLOR_ID_RED,
  283. .brightness = 1,
  284. .intensity = 0,
  285. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1),
  286. },
  287. {
  288. .color_index = LED_COLOR_ID_GREEN,
  289. .brightness = 1,
  290. .intensity = 0,
  291. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0),
  292. },
  293. {
  294. .color_index = LED_COLOR_ID_BLUE,
  295. .brightness = 1,
  296. .intensity = 0,
  297. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7),
  298. },
  299. {
  300. .color_index = LED_COLOR_ID_YELLOW,
  301. .brightness = 1,
  302. .intensity = 0,
  303. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6),
  304. },
  305. };
  306. static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = {
  307. {
  308. .color_index = LED_COLOR_ID_RED,
  309. .brightness = 1,
  310. .intensity = 0,
  311. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5),
  312. },
  313. {
  314. .color_index = LED_COLOR_ID_GREEN,
  315. .brightness = 1,
  316. .intensity = 0,
  317. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4),
  318. },
  319. {
  320. .color_index = LED_COLOR_ID_BLUE,
  321. .brightness = 1,
  322. .intensity = 0,
  323. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3),
  324. },
  325. {
  326. .color_index = LED_COLOR_ID_YELLOW,
  327. .brightness = 1,
  328. .intensity = 0,
  329. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2),
  330. },
  331. };
  332. static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = {
  333. {
  334. .color_index = LED_COLOR_ID_RED,
  335. .brightness = 1,
  336. .intensity = 0,
  337. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1),
  338. },
  339. {
  340. .color_index = LED_COLOR_ID_GREEN,
  341. .brightness = 1,
  342. .intensity = 0,
  343. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0),
  344. },
  345. {
  346. .color_index = LED_COLOR_ID_BLUE,
  347. .brightness = 1,
  348. .intensity = 0,
  349. .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1),
  350. },
  351. {
  352. .color_index = LED_COLOR_ID_YELLOW,
  353. .brightness = 1,
  354. .intensity = 0,
  355. .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0),
  356. },
  357. };
  358. static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = {
  359. {
  360. .led_cdev = {
  361. .name = "platled::wan",
  362. .brightness = 0,
  363. .max_brightness = 1,
  364. .brightness_set = silicom_mec_led_mc_brightness_set,
  365. .brightness_get = silicom_mec_led_mc_brightness_get,
  366. },
  367. .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info),
  368. .subled_info = plat_0222_wan_mc_subled_info,
  369. },
  370. {
  371. .led_cdev = {
  372. .name = "platled::sys",
  373. .brightness = 0,
  374. .max_brightness = 1,
  375. .brightness_set = silicom_mec_led_mc_brightness_set,
  376. .brightness_get = silicom_mec_led_mc_brightness_get,
  377. },
  378. .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info),
  379. .subled_info = plat_0222_sys_mc_subled_info,
  380. },
  381. {
  382. .led_cdev = {
  383. .name = "platled::stat1",
  384. .brightness = 0,
  385. .max_brightness = 1,
  386. .brightness_set = silicom_mec_led_mc_brightness_set,
  387. .brightness_get = silicom_mec_led_mc_brightness_get,
  388. },
  389. .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info),
  390. .subled_info = plat_0222_stat1_mc_subled_info,
  391. },
  392. {
  393. .led_cdev = {
  394. .name = "platled::stat2",
  395. .brightness = 0,
  396. .max_brightness = 1,
  397. .brightness_set = silicom_mec_led_mc_brightness_set,
  398. .brightness_get = silicom_mec_led_mc_brightness_get,
  399. },
  400. .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info),
  401. .subled_info = plat_0222_stat2_mc_subled_info,
  402. },
  403. {
  404. .led_cdev = {
  405. .name = "platled::stat3",
  406. .brightness = 0,
  407. .max_brightness = 1,
  408. .brightness_set = silicom_mec_led_mc_brightness_set,
  409. .brightness_get = silicom_mec_led_mc_brightness_get,
  410. },
  411. .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info),
  412. .subled_info = plat_0222_stat3_mc_subled_info,
  413. },
  414. { },
  415. };
  416. static struct gpio_chip silicom_gpio_chip = {
  417. .label = "silicom-gpio",
  418. .get_direction = silicom_gpio_get_direction,
  419. .direction_input = silicom_gpio_direction_input,
  420. .direction_output = silicom_gpio_direction_output,
  421. .get = silicom_gpio_get,
  422. .set = silicom_gpio_set,
  423. .base = -1,
  424. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  425. .names = plat_0222_gpio_names,
  426. /*
  427. * We're using a mutex to protect the indirect access, so we can sleep
  428. * if the lock blocks
  429. */
  430. .can_sleep = true,
  431. };
  432. static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = {
  433. .io_base = MEC_IO_BASE,
  434. .io_len = MEC_IO_LEN,
  435. .led_info = plat_0222_mc_led_info,
  436. .gpiochip = &silicom_gpio_chip,
  437. .gpio_channels = plat_0222_gpio_channels,
  438. /*
  439. * The original generic cordoba does not have the last 4 outputs of the
  440. * plat_0222 variant, the rest are the same, so use the same longer list,
  441. * but ignore the last entries here
  442. */
  443. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  444. };
  445. static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = {
  446. {
  447. .color_index = LED_COLOR_ID_RED,
  448. .brightness = 1,
  449. .intensity = 0,
  450. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6),
  451. },
  452. {
  453. .color_index = LED_COLOR_ID_GREEN,
  454. .brightness = 1,
  455. .intensity = 0,
  456. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5),
  457. },
  458. {
  459. .color_index = LED_COLOR_ID_BLUE,
  460. .brightness = 1,
  461. .intensity = 0,
  462. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7),
  463. },
  464. {
  465. .color_index = LED_COLOR_ID_AMBER,
  466. .brightness = 1,
  467. .intensity = 0,
  468. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4),
  469. },
  470. };
  471. static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = {
  472. {
  473. .color_index = LED_COLOR_ID_RED,
  474. .brightness = 1,
  475. .intensity = 0,
  476. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7),
  477. },
  478. {
  479. .color_index = LED_COLOR_ID_GREEN,
  480. .brightness = 1,
  481. .intensity = 0,
  482. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4),
  483. },
  484. {
  485. .color_index = LED_COLOR_ID_BLUE,
  486. .brightness = 1,
  487. .intensity = 0,
  488. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3),
  489. },
  490. {
  491. .color_index = LED_COLOR_ID_AMBER,
  492. .brightness = 1,
  493. .intensity = 0,
  494. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6),
  495. },
  496. };
  497. static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = {
  498. {
  499. .color_index = LED_COLOR_ID_RED,
  500. .brightness = 1,
  501. .intensity = 0,
  502. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2),
  503. },
  504. {
  505. .color_index = LED_COLOR_ID_GREEN,
  506. .brightness = 1,
  507. .intensity = 0,
  508. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1),
  509. },
  510. {
  511. .color_index = LED_COLOR_ID_BLUE,
  512. .brightness = 1,
  513. .intensity = 0,
  514. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0),
  515. },
  516. {
  517. .color_index = LED_COLOR_ID_AMBER,
  518. .brightness = 1,
  519. .intensity = 0,
  520. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5),
  521. },
  522. };
  523. static struct led_classdev_mc cordoba_mc_led_info[] __initdata = {
  524. {
  525. .led_cdev = {
  526. .name = "platled::fp_left",
  527. .brightness = 0,
  528. .max_brightness = 1,
  529. .brightness_set = silicom_mec_led_mc_brightness_set,
  530. .brightness_get = silicom_mec_led_mc_brightness_get,
  531. },
  532. .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info),
  533. .subled_info = cordoba_fp_left_mc_subled_info,
  534. },
  535. {
  536. .led_cdev = {
  537. .name = "platled::fp_center",
  538. .brightness = 0,
  539. .max_brightness = 1,
  540. .brightness_set = silicom_mec_led_mc_brightness_set,
  541. .brightness_get = silicom_mec_led_mc_brightness_get,
  542. },
  543. .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info),
  544. .subled_info = cordoba_fp_center_mc_subled_info,
  545. },
  546. {
  547. .led_cdev = {
  548. .name = "platled::fp_right",
  549. .brightness = 0,
  550. .max_brightness = 1,
  551. .brightness_set = silicom_mec_led_mc_brightness_set,
  552. .brightness_get = silicom_mec_led_mc_brightness_get,
  553. },
  554. .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info),
  555. .subled_info = cordoba_fp_right_mc_subled_info,
  556. },
  557. { },
  558. };
  559. static struct silicom_platform_info silicom_generic_cordoba_info __initdata = {
  560. .io_base = MEC_IO_BASE,
  561. .io_len = MEC_IO_LEN,
  562. .led_info = cordoba_mc_led_info,
  563. .gpiochip = &silicom_gpio_chip,
  564. .gpio_channels = plat_0222_gpio_channels,
  565. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  566. };
  567. /*
  568. * sysfs interface
  569. */
  570. static ssize_t efuse_status_show(struct device *dev,
  571. struct device_attribute *attr,
  572. char *buf)
  573. {
  574. u32 reg;
  575. mutex_lock(&mec_io_mutex);
  576. /* Select memory region */
  577. outb(IO_REG_BANK, EC_ADDR_MSB);
  578. outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB);
  579. /* Get current data from the address */
  580. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  581. mutex_unlock(&mec_io_mutex);
  582. efuse_status = reg & 0x1;
  583. return sysfs_emit(buf, "%u\n", efuse_status);
  584. }
  585. static DEVICE_ATTR_RO(efuse_status);
  586. static ssize_t uc_version_show(struct device *dev,
  587. struct device_attribute *attr,
  588. char *buf)
  589. {
  590. int uc_version;
  591. u32 reg;
  592. mutex_lock(&mec_io_mutex);
  593. outb(IO_REG_BANK, EC_ADDR_MSB);
  594. outb(DEFAULT_CHAN_LO, EC_ADDR_LSB);
  595. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  596. mutex_unlock(&mec_io_mutex);
  597. uc_version = FIELD_GET(MEC_VERSION_LOC, reg);
  598. if (uc_version >= 192)
  599. return -EINVAL;
  600. uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 +
  601. FIELD_GET(MEC_VERSION_MINOR, reg);
  602. mec_uc_version = uc_version;
  603. return sysfs_emit(buf, "%u\n", mec_uc_version);
  604. }
  605. static DEVICE_ATTR_RO(uc_version);
  606. static ssize_t power_cycle_show(struct device *dev,
  607. struct device_attribute *attr,
  608. char *buf)
  609. {
  610. return sysfs_emit(buf, "%u\n", power_cycle);
  611. }
  612. static void powercycle_uc(void)
  613. {
  614. /* Select memory region */
  615. outb(IO_REG_BANK, EC_ADDR_MSB);
  616. outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB);
  617. /* Set to 1 for current data from the address */
  618. outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  619. }
  620. static ssize_t power_cycle_store(struct device *dev,
  621. struct device_attribute *attr,
  622. const char *buf, size_t count)
  623. {
  624. int rc;
  625. unsigned int power_cycle_cmd;
  626. rc = kstrtou32(buf, 0, &power_cycle_cmd);
  627. if (rc)
  628. return -EINVAL;
  629. if (power_cycle_cmd > 0) {
  630. mutex_lock(&mec_io_mutex);
  631. power_cycle = power_cycle_cmd;
  632. powercycle_uc();
  633. mutex_unlock(&mec_io_mutex);
  634. }
  635. return count;
  636. }
  637. static DEVICE_ATTR_RW(power_cycle);
  638. static struct attribute *silicom_attrs[] = {
  639. &dev_attr_efuse_status.attr,
  640. &dev_attr_uc_version.attr,
  641. &dev_attr_power_cycle.attr,
  642. NULL,
  643. };
  644. ATTRIBUTE_GROUPS(silicom);
  645. static struct platform_driver silicom_platform_driver = {
  646. .driver = {
  647. .name = "silicom-platform",
  648. .dev_groups = silicom_groups,
  649. },
  650. };
  651. static int __init silicom_mc_leds_register(struct device *dev,
  652. const struct led_classdev_mc *mc_leds)
  653. {
  654. int size = sizeof(struct mc_subled);
  655. struct led_classdev_mc *led;
  656. int i, err;
  657. for (i = 0; mc_leds[i].led_cdev.name; i++) {
  658. led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  659. if (!led)
  660. return -ENOMEM;
  661. memcpy(led, &mc_leds[i], sizeof(*led));
  662. led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL);
  663. if (!led->subled_info)
  664. return -ENOMEM;
  665. memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size);
  666. err = devm_led_classdev_multicolor_register(dev, led);
  667. if (err)
  668. return err;
  669. }
  670. return 0;
  671. }
  672. static u32 rpm_get(void)
  673. {
  674. u32 reg;
  675. mutex_lock(&mec_io_mutex);
  676. /* Select memory region */
  677. outb(IO_REG_BANK, EC_ADDR_MSB);
  678. outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
  679. reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  680. mutex_unlock(&mec_io_mutex);
  681. return reg;
  682. }
  683. static u32 temp_get(void)
  684. {
  685. u32 reg;
  686. mutex_lock(&mec_io_mutex);
  687. /* Select memory region */
  688. outb(IO_REG_BANK, EC_ADDR_MSB);
  689. outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
  690. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  691. mutex_unlock(&mec_io_mutex);
  692. return FIELD_GET(MEC_TEMP_LOC, reg) * 100;
  693. }
  694. static umode_t silicom_fan_control_fan_is_visible(const u32 attr)
  695. {
  696. switch (attr) {
  697. case hwmon_fan_input:
  698. case hwmon_fan_label:
  699. return 0444;
  700. default:
  701. return 0;
  702. }
  703. }
  704. static umode_t silicom_fan_control_temp_is_visible(const u32 attr)
  705. {
  706. switch (attr) {
  707. case hwmon_temp_input:
  708. case hwmon_temp_label:
  709. return 0444;
  710. default:
  711. return 0;
  712. }
  713. }
  714. static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val)
  715. {
  716. switch (attr) {
  717. case hwmon_fan_input:
  718. *val = rpm_get();
  719. return 0;
  720. default:
  721. return -EOPNOTSUPP;
  722. }
  723. }
  724. static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val)
  725. {
  726. switch (attr) {
  727. case hwmon_temp_input:
  728. *val = temp_get();
  729. return 0;
  730. default:
  731. return -EOPNOTSUPP;
  732. }
  733. }
  734. static umode_t silicom_fan_control_is_visible(const void *data,
  735. enum hwmon_sensor_types type,
  736. u32 attr, int channel)
  737. {
  738. switch (type) {
  739. case hwmon_fan:
  740. return silicom_fan_control_fan_is_visible(attr);
  741. case hwmon_temp:
  742. return silicom_fan_control_temp_is_visible(attr);
  743. default:
  744. return 0;
  745. }
  746. }
  747. static int silicom_fan_control_read(struct device *dev,
  748. enum hwmon_sensor_types type,
  749. u32 attr, int channel,
  750. long *val)
  751. {
  752. switch (type) {
  753. case hwmon_fan:
  754. return silicom_fan_control_read_fan(dev, attr, val);
  755. case hwmon_temp:
  756. return silicom_fan_control_read_temp(dev, attr, val);
  757. default:
  758. return -EOPNOTSUPP;
  759. }
  760. }
  761. static int silicom_fan_control_read_labels(struct device *dev,
  762. enum hwmon_sensor_types type,
  763. u32 attr, int channel,
  764. const char **str)
  765. {
  766. switch (type) {
  767. case hwmon_fan:
  768. *str = "Silicom_platform: Fan Speed";
  769. return 0;
  770. case hwmon_temp:
  771. *str = "Silicom_platform: Thermostat Sensor";
  772. return 0;
  773. default:
  774. return -EOPNOTSUPP;
  775. }
  776. }
  777. static const struct hwmon_ops silicom_fan_control_hwmon_ops = {
  778. .is_visible = silicom_fan_control_is_visible,
  779. .read = silicom_fan_control_read,
  780. .read_string = silicom_fan_control_read_labels,
  781. };
  782. static const struct hwmon_chip_info silicom_chip_info = {
  783. .ops = &silicom_fan_control_hwmon_ops,
  784. .info = silicom_fan_control_info,
  785. };
  786. static int __init silicom_platform_probe(struct platform_device *device)
  787. {
  788. struct device *hwmon_dev;
  789. u8 magic, ver;
  790. int err;
  791. if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) {
  792. dev_err(&device->dev, "couldn't reserve MEC io ports\n");
  793. return -EBUSY;
  794. }
  795. /* Sanity check magic number read for EC */
  796. outb(IO_REG_BANK, MEC_ADDR);
  797. magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  798. ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI));
  799. dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver);
  800. if (magic != SILICOM_MEC_MAGIC) {
  801. dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic);
  802. return -ENODEV;
  803. }
  804. err = silicom_mc_leds_register(&device->dev, silicom_led_info);
  805. if (err) {
  806. dev_err(&device->dev, "Failed to register LEDs\n");
  807. return err;
  808. }
  809. err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip,
  810. silicom_gpio_channels);
  811. if (err) {
  812. dev_err(&device->dev, "Failed to register gpiochip: %d\n", err);
  813. return err;
  814. }
  815. hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL,
  816. &silicom_chip_info, NULL);
  817. err = PTR_ERR_OR_ZERO(hwmon_dev);
  818. if (err) {
  819. dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err);
  820. return err;
  821. }
  822. return err;
  823. }
  824. static int __init silicom_platform_info_init(const struct dmi_system_id *id)
  825. {
  826. struct silicom_platform_info *info = id->driver_data;
  827. silicom_led_info = info->led_info;
  828. silicom_gpio_channels = info->gpio_channels;
  829. silicom_gpiochip = info->gpiochip;
  830. silicom_gpiochip->ngpio = info->ngpio;
  831. return 1;
  832. }
  833. static const struct dmi_system_id silicom_dmi_ids[] __initconst = {
  834. {
  835. .callback = silicom_platform_info_init,
  836. .ident = "Silicom Cordoba (Generic)",
  837. .matches = {
  838. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  839. DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"),
  840. },
  841. .driver_data = &silicom_generic_cordoba_info,
  842. },
  843. {
  844. .callback = silicom_platform_info_init,
  845. .ident = "Silicom Cordoba (Generic)",
  846. .matches = {
  847. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  848. DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"),
  849. },
  850. .driver_data = &silicom_generic_cordoba_info,
  851. },
  852. {
  853. .callback = silicom_platform_info_init,
  854. .ident = "Silicom Cordoba (plat_0222)",
  855. .matches = {
  856. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  857. DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"),
  858. },
  859. .driver_data = &silicom_plat_0222_cordoba_info,
  860. },
  861. { },
  862. };
  863. MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
  864. static int __init silicom_platform_init(void)
  865. {
  866. if (!dmi_check_system(silicom_dmi_ids)) {
  867. pr_err("No DMI match for this platform\n");
  868. return -ENODEV;
  869. }
  870. silicom_platform_dev = platform_create_bundle(&silicom_platform_driver,
  871. silicom_platform_probe,
  872. NULL, 0, NULL, 0);
  873. return PTR_ERR_OR_ZERO(silicom_platform_dev);
  874. }
  875. static void __exit silicom_platform_exit(void)
  876. {
  877. platform_device_unregister(silicom_platform_dev);
  878. platform_driver_unregister(&silicom_platform_driver);
  879. }
  880. module_init(silicom_platform_init);
  881. module_exit(silicom_platform_exit);
  882. MODULE_LICENSE("GPL");
  883. MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
  884. MODULE_DESCRIPTION("Platform driver for Silicom network appliances");